pax_global_header00006660000000000000000000000064143426652100014515gustar00rootroot0000000000000052 comment=d8f702fb4d44c746bb0edf00643aa7139cb8bdf7 httpcomponents-client-rel-v5.2.1/000077500000000000000000000000001434266521000170315ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/.gitattributes000066400000000000000000000016731434266521000217330ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Auto detect text files and perform LF normalization * text=auto *.java text diff=java *.html text diff=html *.css text *.js text *.serialized binary httpcomponents-client-rel-v5.2.1/.github/000077500000000000000000000000001434266521000203715ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/.github/dependabot.yml000066400000000000000000000023741434266521000232270ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. version: 2 updates: - package-ecosystem: "maven" directory: "/" schedule: interval: "weekly" day: "friday" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" day: "friday" httpcomponents-client-rel-v5.2.1/.github/workflows/000077500000000000000000000000001434266521000224265ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/.github/workflows/codeql-analysis.yml000066400000000000000000000065611434266521000262510ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # ******** NOTE ******** name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '16 7 * * 5' permissions: contents: read jobs: analyze: permissions: actions: read # for github/codeql-action/init to get workflow details contents: read # for actions/checkout to fetch code security-events: write # for github/codeql-action/analyze to upload SARIF results name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'java' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - name: Checkout repository uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) #- name: Autobuild # uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language - run: mvn clean package -DskipTests -Drat.skip=true -Dcheckstyle.skip - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 httpcomponents-client-rel-v5.2.1/.github/workflows/depsreview.yaml000066400000000000000000000020731434266521000254710ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. name: 'Dependency Review' on: [pull_request] permissions: contents: read jobs: dependency-review: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' uses: actions/checkout@v3 - name: 'Dependency Review' uses: actions/dependency-review-action@v3 httpcomponents-client-rel-v5.2.1/.github/workflows/maven.yml000066400000000000000000000040261434266521000242610ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Java CI on: [push, pull_request] permissions: contents: read jobs: build: runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} strategy: matrix: # windows-latest is not used due to intermittent network failures os: [ubuntu-latest, macos-latest] # All LTS versions plus the current version java: [ 8, 11, 17 ] experimental: [false] # include: # - java: 20-ea # os: ubuntu-latest # experimental: true # - java: 20-ea # os: windows-latest # experimental: true # - java: 20-ea # os: macos-latest # experimental: true fail-fast: false steps: - uses: actions/checkout@v3 - uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Build with Maven run: mvn -V --file pom.xml --no-transfer-progress -DtrimStackTrace=false -P-use-toolchains httpcomponents-client-rel-v5.2.1/.gitignore000066400000000000000000000001731434266521000210220ustar00rootroot00000000000000bin .classpath .project .settings .clover .externalToolBuilders target .idea *.iml **/log4j2-debug.xml **/.checkstyle *.bakhttpcomponents-client-rel-v5.2.1/BUILDING.txt000066400000000000000000000021571434266521000207740ustar00rootroot00000000000000Building HttpComponents Client ============================ (1) Requisites -------------- JDK 1.7+ is required in order to compile and run HttpClient. HttpClient utilizes Maven as a distribution management and packaging tool. Version 3.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 and install them to the local repository: mvn install The JAR packages can be found in the target folders of their respective modules httpclient5/target/httpclient5-.jar httpclient5-cache/target/httpclient5-cache-.jar httpclient5-fluent/target/httpclient5-fluent-.jar httpclient5-win/target/httpclient5-win-.jar httpclient5-osgi/target/org.apache.httpcomponents.httpclient_.jar where is the release version (4) Validating packages Check for proper license headers with: mvn apache-rat:check httpcomponents-client-rel-v5.2.1/CODE_OF_CONDUCT.md000066400000000000000000000016411434266521000216320ustar00rootroot00000000000000 The Apache code of conduct page is [https://www.apache.org/foundation/policies/conduct.html](https://www.apache.org/foundation/policies/conduct.html). httpcomponents-client-rel-v5.2.1/LICENSE.txt000066400000000000000000000650751434266521000206710ustar00rootroot00000000000000 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 includes Public Suffix List copied from licensed under the terms of the Mozilla Public License, v. 2.0 Full license text: Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. httpcomponents-client-rel-v5.2.1/NOTICE.txt000066400000000000000000000002671434266521000205600ustar00rootroot00000000000000Apache HttpComponents Client Copyright 1999-2022 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). httpcomponents-client-rel-v5.2.1/README.md000066400000000000000000000103411434266521000203070ustar00rootroot00000000000000 Apache HttpComponents Client ============================ Welcome to the HttpClient component of the Apache HttpComponents project. [![GitHub Actions Status](https://github.com/apache/httpcomponents-client/workflows/Java%20CI/badge.svg)](https://github.com/apache/httpcomponents-client/actions) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.httpcomponents.client5/httpclient5/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.httpcomponents.client5/httpclient5) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) Building Instructions --------------------- For building from source instructions please refer to [BUILDING.txt](./BUILDING.txt). Dependencies ------------ HttpClient main module requires Java 8 compatible runtime and depends on the following external libraries: * [Apache HttpComponents HttpCore](https://github.com/apache/httpcomponents-core) * [SLF4J API](http://www.slf4j.org/) * [Apache Commons Codec](https://github.com/apache/commons-codec) Other dependencies are optional. (for detailed information on external dependencies please see [pom.xml](./pom.xml)) Licensing --------- Apache HttpComponents Client is licensed under the Apache License 2.0. See the files [LICENSE.txt](./LICENSE.txt) and [NOTICE.txt](./NOTICE.txt) for more information. Contact ------- - For general information visit the main project site at https://hc.apache.org/ - For current status information visit the status page at https://hc.apache.org/status.html - If you want to contribute visit https://hc.apache.org/get-involved.html 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 https://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 Client interfaces with the > Java Secure Socket Extension (JSSE) API to provide > - HTTPS support > > Apache HttpComponents Client does not include any > implementation of JSSE. httpcomponents-client-rel-v5.2.1/RELEASE_NOTES.txt000066400000000000000000004065601434266521000216350ustar00rootroot00000000000000Release 5.2.1 ------------------ This is a maintenance release that fixes several regressions found in release 5.2. Change Log ------------------- * Regression: Async execution runtimes set the negotiated protocol version in the execution context at the wrong point of request execution. Contributed by Oleg Kalnichevski * Cancel connection request on exception. Contributed by Bryan Keller * Replace deprecated use of LangUtils#equals() with Objects.equals(). Contributed by Gary Gregory * Regression: Multipart body builder and multipart formatters fail to escape special characters such as backslash and quote mark. Contributed by Oleg Kalnichevski Release 5.2 ------------------ This is the first GA release in the 5.2 release series. This release finalizes the 5.2 APIs and corrects a number of defects discovered since the previous release. Please note that 5.2 upgrades the minimal JRE level to version 8 (8u251 is required). Please note this is likely to be the last release series with support for SPNEGO and NTLM authentication. As of version 5.3 GSS-API-based authentication schemes (Kerberos, SPNEGO) and NTLM authentication schemes are going to be deprecated and disabled by default. Notable changes and features included in the 5.2 series: * Upgrade to Java 8. * Improved support for TLS upgrade and HTTP protocol upgrade (async). * Support for H2 tunneling via HTTP/1.1 proxy. * Conformance to RFC 7617 (The 'Basic' HTTP Authentication Scheme). * Migration to Java 8 Time primitives in State Management and Cache APIs. * Connection and TLS configuration on a per route basis. * Base64 codec based on Commons Codec replaced with JRE Base64 codec. Dependency on Commons Codec dropped. * Optional support for BR (Brotli) decompression. Change Log ------------------- * HTTPCLIENT-2242: RoutingSupport fails to copy InetAddress when normalizing HttpHost. Contributed by Oleg Kalnichevski * HTTPCLIENT-2240: Fixed incorrect CONNECT method initialization in ProxyClient. Contributed by Oleg Kalnichevski * HTTPCLIENT-2236: MultihomeIOSessionRequester fails to enhance the cause exception in case of connect failure if the remoteAddress argument has been given. Contributed by Oleg Kalnichevski * Use Objects.toString() instead of String type cast Contributed by Gary Gregory * HTTPCLIENT-2232: Last protocol interceptors moved at the end of the H2 protocol processing pipeline. Contributed by Oleg Kalnichevski * H2 async runtime to proactively set HTTP/2 protocol version in the execution context. Contributed by Oleg Kalnichevski * HTTPCLIENT-2231: Fixed a race condition in the main async executor when the request execution on an I/O thread is faster than execution pipeline management on the client thread. Contributed by Oleg Kalnichevski * Avoid duplicate redundant objects and use Singleton instead. Contributed by Arturo Bernal * HTTPCLIENT-2225: Connection route calculation does not take the default RequestConfig into account. Contributed by Oleg Kalnichevski * Avoid unnecessary use of Instant.toEpochMilli by using Instant#compareTo to compare Instants directly. Contributed by jkmcl * HTTPCLIENT-2221 Closing a classic response/entity allows connection reuse. Contributed by Carter Kozak Release 5.2 BETA1 ------------------ This is the first BETA release in the 5.2 release series that upgrades minimal JRE level to version 8 (8u251 is required) and includes several protocol level and API improvements. It also includes all bug fixes from the 5.1 branch. Change Log ------------------- * Upgraded HttpCore to version 5.2-beta2. Contributed by Oleg Kalnichevski * HTTPCLIENT-2218: Use Java 8 Base64 utility (#370). Contributed by j3graham * Added support for BR (Brotli) decompression (#363). Contributed by 殷成涛 * HTTPCLIENT-2212: MinimalHttpAsyncClient fails to release client endpoints in case of a connect error (such as TLS handshake failure). Contributed by Oleg Kalnichevski * InternalAbstractHttpAsyncClient to create daemin threads. Contributed by Richard Hernandez * HTTPCLIENT-2080: Added #getRetryInterval method to HttpRequestRetryStrategy for use on retriable IOExceptions (#356). Contributed by Anthony Baldocchi <489445+ajbaldocchi at users.noreply.github.com> * Fixed infinite recursion in SSLConnectionSocketFactory. Contributed by Ryan Schmitt * HTTPCLIENT-2200: Protocol interceptors are executed before the connection route has been fully established. Contributed by Oleg Kalnichevski * HTTPCLIENT-2209: Pass HttpContext to AsyncClientConnectionOperator (#353). Contributed by Andriy Redko * HTTPCLIENT-2206: Corrected resource de-allocation by fluent response objects. Contributed by Oleg Kalnichevski * ExecSupport#getNextExchangeId() optimization (#352) Contributed by David Schlosnagle * HTTPCLIENT-2203: Corrected target host normalization by the request execution interceptors; added ContextBuilder with support for preemptive authentication initialization. Contributed by Oleg Kalnichevski * HTTPCLIENT-2202: MemcachedHttpCacheStorage to support MemcachedClientIF interface. Contributed by Oleg Kalnichevski * Bug fix: ByteArrayBuilder incorrectly handles empty strings. Contributed by Oleg Kalnichevski * HTTPCLIENT-2198: Fixed AbstractClientTlsStrategy to respect HttpVersionPolicy. Contributed by Andrei Vasilev <59628447+AndreiSVasilev at users.noreply.github.com> * Updated AbstractClientTlsStrategy to pass only the HttpVersionPolicy set by TlsConfig instead of the entire TlsConfig to H2TlsSupport#selectApplicationProtocols() method. Contributed by Andrei Vasilev <59628447+AndreiSVasilev at users.noreply.github.com> * HTTPCLIENT-2195, regression: Classic ConnectExec incorrectly discards the proxy response body even if the request cannot be executed and the response is final. Contributed by Oleg Kalnichevski * HTTPCLIENT-2194: Async retry request interceptor fails to correct include request body on retry (#343). Contributed by JasonMathison * Deprecated execute methods that return an open response object in favor of execute methods with a response handler and automatic resource deallocation. Contributed by Oleg Kalnichevski * HTTPCLIENT-2189: Cookie and Cache APIs to use Java time primitives. Contributed by Arturo Bernal * Apply English locale to all date header formatters. Contributed by Michael Osipov * HTTPCLIENT-2184: Fixed an issue in which connections were not returned to the pool when requests contained non-repeatable bodies AND responses were streamed. Contributed by Carter Kozak Release 5.2 ALPHA1 ------------------ This is the first ALPHA release in the 5.2 release series that upgrades minimal JRE level to version 1.8 (8u251 is required) and includes several protocol level and API improvements. It also includes all bug fixes from the 5.1 branch. Notable changes and features included in the 5.2 series: * Upgrade to Java 8. * Improved support for TLS upgrade and HTTP protocol upgrade (async). * Support for H2 tunneling via HTTP/1.1 proxy. * Conformance to RFC 7617 (The 'Basic' HTTP Authentication Scheme). Change Log ------------------- * Replaced SimpleDateFormat and Calendar with Java 8 Time APIs; removed thread-local from DateUtils. Contributed by Oleg Kalnichevski * Support for connection 'total time to live' setting on a per-route basis. Contributed by Oleg Kalnichevski * Configurable IOReactor IO session decorator configurable. Contributed by Arturo Bernal * HTTPCLIENT-2182: access to SSLSession attributes via reflection disallowed as of Java 16. Core TLS functions now use new Java 1.8 API introduced by 8u251 update. Contributed by Oleg Kalnichevski * HTTPCLIENT-2135: TLS configuration on a per-host basis. Contributed by Oleg Kalnichevski * RFC 7230: treat presence of userinfo in authority component in request URI as an HTTP protocol violation. Contributed by Oleg Kalnichevski * AuthCache conformance to RFC 7617. Contributed by Oleg Kalnichevski * Added immutable CredentialsProvider implementations and a CredentialsProvider builder. Contributed by Oleg Kalnichevski * HTTPCLIENT-2045: BASIC auth scheme conformance to RFC 7617. Contributed by Oleg Kalnichevski * HTTPCLIENT-2120: support for H2 via HTTP/1.1 proxy. Contributed by Oleg Kalnichevski * Moved connection management related settings from RequestConfig to new class ConnectionConfig. Contributed by Oleg Kalnichevski * HTTPCLIENT-2139: Cookie Header HttpOnly attribute. Contributed by Arturo Bernal Release 5.1.1 ----------- This release upgrades HttpCore to the latest 5.1 version and fixes a number of issues found since release 5.1. Change Log ------------------- * Don't initialize AtomicReference to its default value. Contributed by Gary Gregory * Corrected resolution of the target host in DefaultUserTokenHandler. Contributed by Oleg Kalnichevski * HTTPCLIENT-2177: automatically force HTTP/1.1 protocol policy when executing requests via a proxy tunnel. Contributed by Oleg Kalnichevski * HTTPCLIENT-2177: fixed incorrect route state tracking by the async connect executor when negotiating a tunnel via a proxy. Contributed by Oleg Kalnichevski * HTTPCLIENT-2177: keep successful tunnel connections alive regardless of `Connection: close`. Contributed by Oleg Kalnichevski * HTTPCLIENT-2173: async pooling connection manager to close half-open connection gracefully. Contributed by Oleg Kalnichevski * Don't retry a request for NoRouteToHostException. Contributed by Jaikiran Pai * HTTPCLIENT-2170: Classic protocol layer no longer releases the underlying connection back to the pool prematurely while the NTLM handshake is still ongoing. Contributed by Oleg Kalnichevski * Fixed connection lease request cancellation race in both classic and asyc pooling connection managers. Contributed by Oleg Kalnichevski Release 5.1 ----------- This is the first GA release in the 5.1 release series. Notable changes and features included in the 5.1 series: * Conditional conformance with RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax). * Improved support for out of sequence response message handing by the the classic (blocking) HTTP transport. * Improved message builders. Please note that 5.1 is going to be the last release series compatible with Java 1.7. HttpClient will require Java 1.8 as of 5.2. Change Log ------------------- * HTTPCLIENT-2157: Response object generated by the classic caching backend is missing the original content encoding. Contributed by Oleg Kalnichevski * HTTPCLIENT-2152: Fixed handling of unexpected unchecked exception by the async request retry exec interceptor. Contributed by Oleg Kalnichevski * Async clients to support scheduled (delayed) re-execution of requests. Contributed by Oleg Kalnichevski * HTTPCLIENT-2148: fluent Executor volatile access thread safety (#301). Contributed by Carter Kozak * HTTPCLIENT-2149: When no dNSName, match against CN. Contributed by Peter Dettman * HTTPCLIENT-2147: fixed broken preemptive auth in HC Fluent. Contributed by Robert Rodewald * HTTPCORE-672: cleanup of H2 connection validation code. Contributed by Oleg Kalnichevski * HttpAsyncClientBuilder: Make IOReactor exception callback configurable Contributed by Ryan Schmitt * HTTPCLIENT-2141: HttpClient to not retry requests if the retry interval exceeds the response timeout. Contributed by Oleg Kalnichevski * Fixed NPE during dispose in Response if entity is null Contributed by Sandeep Kulkarni * Blocking connection managers to validate connections after inactivity of more than 2s by default. Contributed by Oleg Kalnichevski * Deprecated request factory classes in favor of request builders. Contributed by Oleg Kalnichevski * HTTPCLIENT-2140: Upgraded Commons Codec to version 1.15. Contributed by Oleg Kalnichevski * Deprecated message copiers in favor of generic message builders. Contributed by Oleg Kalnichevski * Kerberos/SPNego fixes Contributed by Carey Lin Release 5.1 BETA1 ------------------ This is the first BETA release in the 5.1 release series that includes a number of new features as well performance optimizations in the classic HTTP transport. Notable changes and features included in the 5.1 series: * Conditional conformance with RFC 3986 (Uniform Resource Identifier (URI): Generic Syntax). * Improved support for out of sequence response message handing by the the classic (blocking) HTTP transport. Changelog: ------------------- * RFC 3986 conformance: URIUtils to re-use URIBuilder functionality. Contributed by Oleg Kalnichevski * Improved I/O session and wire logging. Contributed by Oleg Kalnichevski * Add interceptors before MAIN_TRANSPORT so they won't be ignored (#272) Contributed by Rob Spoor * HTTPCLIENT-2104: ManagedHttpClientConnectionFactory to support ResponseOutOfOrderStrategy configuration. Contributed by Carter Kozak * Fixed NPE when H2/Async client interceptors are added using first/last (#268). Contributed by Koji Lin * HTTPCLIENT-2126: `InternalAbstractHttpAsyncClient` incorrectly handles response messages with no enclosed entity. Contributed by Oleg Kalnichevski * HTTPCLIENT-2124: Fixed NOE in MinimalHttpClient#doExecute (#261). Contributed by Gary Gregory * HTTPCLIENT-2122: async client to throw `HTTP/2 tunneling not supported` protocol exception in case of `force HTTP/2` version policy used along with request proxy routing. Contributed by Oleg Kalnichevski * HTTPCLIENT-2123: H2AsyncClientBuilder incorrectly adds last request interceptors to the head of the interceptor list. Contributed by Oleg Kalnichevski * Use decimal numbers for endpoint/execution IDs (#249). Contributed by Michael Osipov * HTTPCLIENT-2106: Added charset parameter for DigestScheme. Contributed by Oleg Kalnichevski * HTTPCLIENT-2103: ManagedHttpClientConnectionFactory provides a fluent builder Contributed by Carter Kozak Release 5.0.3 ----------------- This release upgrades HttpCore to the latest version, improves conformance to RFC 7235 (Hypertext Transfer Protocol (HTTP/1.1): Authentication) and addresses a number of issues found since 5.0.2 release. Changelog: ------------------- * PR #270: Master try w res and more. - Use try-with-resources. - Use Arrays.fill(). - Add missing @override. - Simplify if/else. - Remove redundant modifiers. … - Remove redundant returns. Contributed by Gary Gregory Release 5.0.2 ----------------- This release upgrades HttpCore to the latest version, improves conformance to RFC 7235 (Hypertext Transfer Protocol (HTTP/1.1): Authentication) and addresses a number of issues found since 5.0.1 release. Changelog: ------------------- * HTTPCLIENT-2116: Incorrect request message composition when routing requests via a proxy. Contributed by Oleg Kalnichevski * PoolingAsyncClientConnectionManager incorrectly emits Ping commands to HTTP/1.1 endpoints (#255). Contributed by 滕杰1 * HTTPCLIENT-2115: HttpAsyncClientBuilder and H2AsyncClientBuilder fail to take `replaceExecInterceptor()` into account. Contributed by Oleg Kalnichevski * HTTPCLIENT-2112: AbstractMultipartFormat respects ByteBuffer.arrayOffset (#253). Contributed by Carter Kozak * Avoid updating Content-Length header in a 304 response. Contributed by Dirk Henselin * HTTPCLIENT-2105: Async clients incorrectly handle redirects of requests with enclosed entity. Contributed by Oleg Kalnichevski * HTTPCLIENT-2100: Incorrect handling of EXTENDED mode by MultipartEntityBuilder Contributed by Oleg Kalnichevski * HTTPCLIENT-2099, HTTPCLIENT-2091: SSLConnectionSocketFactory connect timeout fix (#241). Contributed by Carter Kozak * Bug fix: BasicExpiresHandler is annotated as immutable but is not (#240). Contributed by Gary Gregory * HTTPCLIENT-2096: Migrate instance loggers to static fields Contributed by Carter Kozak * Added Automatic-Module-Name to the artefact manifests. Contributed by Niels Basjes * MultipartEntityBuilder#generateBoundary optimization (#233) Contributed by slisaasquatch * HTTPCLIENT-2094: ConnectionManager validateAfterInactivity zero duration agreement Contributed by Carter Kozak * RFC 7235 compliance, HTTPCLIENT-2086: Fixed parsing of token68 based (base64-encoded) auth schemes. Contributed by Oleg Kalnichevski * HTTPCLIENT-2091: Connect timeout is used instead of socket timeout after a tls upgrade Contributed by Oleg Kalnichevski * HTTPCLIENT-2084: Client builders incorrectly add message interceptors with LAST position to the head of the list. Contributed by Oleg Kalnichevski * HTTPCLIENT-2083: Fix NPE when classic client interceptors are added. Contributed by Carter Kozak Release 5.0.1 ----------------- This release upgrades HttpCore to the latest version and addresses a number of issues found since 5.0 release. Changelog: ------------------- * Bug fix: Classic connection managers fail to take #isConsistent() flag into account when re-using persistent connections. 7 Contributed by Oleg Kalnichevski * HTTPCLIENT-2077: Authentication failure due to incorrect NTLM auth value check. Contributed by vonahok <64310078+vonahok at users.noreply.github.com> * HTTPCLIENT-2051: Corrected handling of 303 redirects. Contributed by Oleg Kalnichevski * HTTPASYNC-160: HttpAsyncClient in INACTIVE or STOPPED state throws a IllegalStateException causing the current thread to terminate. Contributed by Oleg Kalnichevski * HTTPCLIENT-2076: Fixed NPE in LaxExpiresHandler. Contributed by heejeongkim * HTTPCLIENT-2074: Disallow direct execution of CONNECT methods by standard client implementations. Contributed by Oleg Kalnichevski * HTTPCLIENT-2075: New method Request.responseTimeout(Timeout) in Fluent HC. Contributed by Ralph * HTTPCLIENT-2073: (regression) WindowsNegotiateScheme incorrectly rejects empty NTLM challenge. Contributed by Oleg Kalnichevski * HTTPCLIENT-2069: RequestConfig#copy does not copy #responseTimeout. Contributed by Oleg Kalnichevski * HTTPCLIENT-2061: Corrected sequence of request execution interceptors in classic HttpClient. Contributed by Oleg Kalnichevski * Fixed NPE for null HttpContext in minimal async clients. Contributed by slisaasquatch Release 5.0 ----------------- This is the first stable (GA) release of HttpClient 5.0. Notable changes and features included in the 5.0 series are: * Support for the HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification documents (RFC 7540, RFC 7541.) Supported features: ** HPACK header compression ** Stream multiplexing (client and server) ** Flow control ** Response push ** Message trailers ** Expect-continue handshake ** Connection validation (ping) ** Application-layer protocol negotiation (ALPN) ** TLS 1.2 security features * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification documents (RFC 7230, RFC 7231.) * New connection pool implementation with lax connection limit guarantees and better performance under higher concurrency due to absence of a global pool lock. * Support for Reactive Streams API [http://www.reactive-streams.org/] * Package name space changed to 'org.apache.hc.client5'. * Maven group id changed to 'org.apache.httpcomponents.client5'. HttpClient 5.0 releases can be co-located with earlier major versions on the same classpath due to the change in package names and Maven module coordinates. Changelog: ------------------- * Removed work-around for resumed TLS sessions given that JDK-8212885 fix has been ported to Java 11 and released in Oracle JDK 11.0.3. Contributed by Oleg Kalnichevski * Upgraded HttpCore dependency to version 5.0 Contributed by Oleg Kalnichevski * DefaultHttpRequestRetryStrategy: Allow zero retry interval Contributed by Ryan Schmitt * HTTPCLIENT-2047: fixed regression in DefaultHostnameVerifier causing rejection of certs with non-standard domains. Contributed by Oleg Kalnichevski * GitHub #204: Build requests from method names in ClassicHttpRequests: ClassicHttpRequests.create(String, String) ClassicHttpRequests.create(String, URI) Contributed by Gary Gregory * GitHub #205: Update request factory classes with matching APIs for Method and String method name inputs: BasicHttpRequests.create(String, URI) BasicHttpRequests.create(String, String) ClassicHttpRequests.create(Method, String) ClassicHttpRequests.create(Method, URI) SimpleHttpRequests.create(String, URI) SimpleHttpRequests.create(String, String) Contributed by Gary Gregory * GitHub #208: Do not use input type names in method names: SimpleHttpRequest: Delete setBodyBytes(byte[], ContentType) in favor of setBody(byte[], ContentType) SimpleHttpRequest: Delete setBodyText(String, ContentType) in favor of setBody(String, ContentType) SimpleHttpResponse: Delete setBodyBytes(byte[], ContentType) in favor of setBody(byte[], ContentType) SimpleHttpResponse: Delete setBodyText(String, ContentType) in favor of setBody(String, ContentType) Contributed by Gary Gregory Release 5.0-BETA7 ----------------- This BETA release upgrades HttpCore to the latest version and addresses a number of issues found since the previous BETA release. IMPORTANT: This release is expected to be the last BETA version. If no major design flaws are found the actual 5.0 API will be frozen and the next version will be promoted to GA. Changelog: ------------------- * Improved domain name normalization by DefaultHostnameVerifier. Contributed by Oleg Kalnichevski * DefaultHostnameVerifier: Match DNS and CN names against ICANN domains. Contributed by Ryan Schmitt * HTTPCORE-615: Implement HTTP-based cache serializer-deserializer. Contributed by Scott Gifford * HTTPCLIENT-2040: Copy headers from the original request to the redirect request. Contributed by Oleg Kalnichevski * Removed RFC 2965 specific requirements deprecated and superseded by RFC 6265. Contributed by Oleg Kalnichevski * HTTPCLIENT-2035: Remove HttpRequestRetryHandler in favor of HttpRequestRetryStrategy. Contributed by Michael Osipov * HTTPCLIENT-2019: Remove ServiceUnavailableRetryStrategy in favor of HttpRequestRetryStrategy. Contributed by Michael Osipov * HTTPCLIENT-2034: Introduce HttpRequestRetryStrategy. Contributed by Michael Osipov * CloseableHttpAsyncClient to support explicit HttpHost execution parameter. Contributed by Oleg Kalnichevski * HTTPCLIENT-2020: DefaultBackoffStrategy to support TOO_MANY_REQUESTS (429). Contributed by Michael Osipov * HTTPCLIENT-2030: Fixed PublicSuffixMatcher#getDomainRoot behavior with invalid hostnames. Contributed by Niels Basjes * HTTPCLIENT-2028: Connection managers to allow 0 for `validateAfterInactivity` time value. Contributed by Peter Frank * HTTPCLIENT-2023: Allow nested arrays and all primitive types in DefaultHttpCacheEntrySerializer. Contributed by Olof Larsson Release 5.0-BETA6 ------------------- This BETA release picks up the latest fixes and performance improvements from HttpCore and addresses a number of issues found since the previous BETA release. Changelog: ------------------- * Fixed fallback PublicSuffixMatcher. Contributed by Ryan Schmitt * Enforce h2 TLS rules after negotiating TLS, not before. Contributed by Ryan Schmitt * HTTPCLIENT-2013: Revised handling of connect exceptions; improved consistency in behavior of the classic and async clients; ConnectTimeoutException now extends SocketTimeoutException. Contributed by Oleg Kalnichevski * Improved handling of request cancellation (classic API). Contributed by Oleg Kalnichevski * Fixed concurrent use of threading unsafe ClassicHttpRequest messages. Contributed by Oleg Kalnichevski * Execute Socket#connect under doPrivileged. Contributed by Simon Willnauer * HTTPCLIENT-2009: Fixed StringIndexOutOfBoundsException in AuthSupport#extractFromAuthority. Contributed by itonyli <429284840 at qq.com> * Make Accept-Encoding header handling thread-safe. Contributed by Linton Miller Release 5.0-BETA5 ------------------- This BETA release picks up the latest fixes and performance improvements from HttpCore and addresses a number of issues found since the previous BETA release. IMPORTANT: This release is expected to be the last BETA version. If no major design flaws are found the actual 5.0 API will be frozen and the next version will be promoted to GA. Changelog: ------------------- * Information response (1xx) processing support. Contributed by Kirill Usov * HTTPCLIENT-1968: Preserve escaped PATHSAFE characters when normalizing URI path segments. Contributed by Oleg Kalnichevski * HTTPCLIENT-1992: Impossible to access trailer-headers available in chunked transfer-encoding with classic API. Contributed by Serkan Turgut * HTTPCLIENT-1991: incorrect handling of non-standard DNS entries by PublicSuffixMatcher. Contributed by Oleg Kalnichevski * Refactor to enable support for non-form based multipart requests Contributed by Adam Retter * HTTPCLIENT-1981: disallow TRACE requests with an enclosed entity Contributed by Jay Modi Release 5.0-BETA4 ------------------- This BETA release picks up the latest fixes and performance improvements from HttpCore and addresses a number of issues found since the previous BETA release. Notable features in this release: * Security improvements. * URI handling improvements. Changelog: ------------------- * HTTPCLIENT-1976: Unsafe deserialization in DefaultHttpCacheEntrySerializer. Contributed by Artem Smotrakov * HTTPCLIENT-1969: Filter out weak cipher suites. Contributed by Artem Smotrakov * HttpClient should not retry requests in case of ConnectionClosedException Contributed by Oleg Kalnichevski * Bug fix: Simple response consumer to discard stored content when releasing resources. Contributed by Oleg Kalnichevski * Bug fix: main async request execution handlers to release the associated response consumer upon exception. Contributed by Oleg Kalnichevski * Update Apache Commons Codec from 1.11 to 1.12. Contributed by Gary Gregory * Update RxJava from 2.2.2 to 2.2.7. Contributed by Gary Gregory * Update JNA from 5.0.0 to 5.2.0. Contributed by Gary Gregory * Some well known proxies respond with Content-Length=0, when returning 304. For robustness, always use the cached entity's content length, as modern browsers do. Contributed by Jayson Raymond * HTTPCLIENT-1960: URIBuilder incorrect handling of multiple leading slashes in path component. Contributed by Oleg Kalnichevski * HTTPCLIENT-1958: PoolingHttpClientConnectionManager to throw ExecutionException in case of a lease operation cancellation instead of InterruptedException. Contributed by Oleg Kalnichevski * Shutdown executorService on AbstractHttpAsyncClientBase shutdown. Contributed by 吴雪山 * [HTTPCLIENT-1952: Allow default User Agent to be disabled Contributed by Michael Osipov * Improve HttpResponseException#getMessage. Contributed by Michael Osipov * Better handling of http(s).proxyUser and http(s).proxyPassword Contributed by Jens Borgland * Wrong argument name in PoolingAsyncClientConnectionManagerBuilder#setConnPoolPolicy results with self assignment of variable. Contributed by Eryk Szymanski Release 5.0-BETA3 ------------------- This BETA release adds support for advanced TLS functions (such as ALPN protocol negotiation) on Java 1.7 and Java 1.8 through Conscrypt TLS library and picks up the latest fixes and performance improvements from HttpCore. Notable features in this release: * TLS ALPN protocol negotiation support on older JREs through Conscrypt TLS library. Changelog: ------------------- * Added optional dependency on conscrypt-openjdk-uber 1.4.1; support for advanced TLS functions (such as ALPN extension) on Java 1.7 and Java 1.8 through Conscrypt TLS library Contributed by Oleg Kalnichevski * Removed OSGi module Contributed by Oleg Kalnichevski * Removed experimental CredSsp auth scheme Contributed by Oleg Kalnichevski * HTTPCLIENT-1949: DigestScheme to use HttpRequest#getRequestUri instead of HttpRequest#getPath Contributed by Oleg Kalnichevski Release 5.0-BETA2 ------------------- This BETA release resolves compatibility issues with Java 11 new TLS engine as well as a number of defects found since the previous release. Notable new features in this release: * JDK 11 compatibility * Support for request specific push consumers * Support for Reactive Streams API [http://www.reactive-streams.org/] Changelog: ------------------- * Hack to make hostname verification work with TLSv1.3 resumed sessions. For details see https://markmail.org/message/mxf5v2d2gh6ws2j3 Contributed by Oleg Kalnichevski * Added builders for SSLConnectionSocketFactory and client TlsStrategy. Contributed by Oleg Kalnichevski * Added enum for supported TLS versions & TLS version parser. Contributed by Oleg Kalnichevski * HTTPCLIENT-1946: handling of 308 status as per RFC 7538. Contributed by Oleg Kalnichevski * Upgraded HttpCore to version 5.0-beta5. Contributed by Oleg Kalnichevski * Async clients to support request specific push consumers. Contributed by Oleg Kalnichevski * HTTPCLIENT-1944: Add hardCancellationEnabled option to RequestConfig. Contributed by Ryan Schmitt * Overload Request's execute method to allow custom CloseableHttpClient. Contributed by Nicolas Gomez * Fix HttpClient 4.5.4 regression in BasicCookieStore serialization. Contributed by Mark Mielke * HTTPCLIENT-1934: Default client TLS strategy passes wrong hostname to the hostname verifier. Contributed by Oleg Kalnichevski * HTTPCLIENT-1882: reset authentication state on I/O or runtime error for connection based authentication schemes (such as NTLM). Contributed by Oleg Kalnichevski * HTTPCLIENT-1924: HttpClient to shut down the connection manager if a fatal error occurs in the course of a request execution. Contributed by Oleg Kalnichevski * Pooling connection managers to implement graceful and immediate shut down. Contributed by Oleg Kalnichevski * HTTPCLIENT-1906: certificates containing alternative subject names other than DNS and IP (such as RFC822) get rejected as invalid. Contributed by Oleg Kalnichevski * HTTPCLIENT-1904: check cookie domain for null Contributed by Hans-Peter Keck * HTTPCLIENT-1900: proxy protocol processor in the CONNECT exec interceptor does not post-process CONNECT response messages. Contributed by Oleg Kalnichevski * HTTPCLIENT-1898: Incorrect comment in example class ClientMultiThreadedExecution.java Contributed by Ulrich Romahn * HTTPCLIENT-1931: Add factory enum org.apache.hc.client5.http.classic.methods.ClassicHttpRequests Contributed by Gary Gregory * HTTPCLIENT-1932: Add factory enum org.apache.hc.client5.http.async.methods.HttpRequests Contributed by Gary Gregory * HTTPCLIENT-1939: Update Apache Commons Codec from 1.10 to 1.11 Contributed by Gary Gregory * HTTPCLIENT-1947: Update JNA from 4.5.2 to 5.0.0 Contributed by Gary Gregory Release 5.0-BETA1 ------------------- This is the first BETA release of HttpClient 5.0. The 5.0 release serices introduces support for the HTTP/2 protocol and event driven messaging APIs consistent for all supported HTTP protocol versions. Changelog: ------------------- * DefaultHostnameVerifier to use a custom distinguished name (DN) parser instead of LdapName. Removed dependency on Java Naming extensions. Contributed by Oleg Kalnichevski * HTTP/2 client implementations to support cancellation of ongoing message exchanges without terminating the underlying connections. Contributed by Oleg Kalnichevski * HTTPCLIENT-1395: added config parameter to skip an extra cache entry freshness check upon cache update in case of a cache miss. Contributed by Oleg Kalnichevski * HTTPCLIENT-1824, HTTPCLIENT-1384: asynchronous HTTP cache invalidator. Contributed by Oleg Kalnichevski * Redesign of CacheKeyGenerator and HttpCacheInvalidator APIs. Contributed by Oleg Kalnichevski * New APIs for cache entry bulk retrieval; bulk retrieval support by Memcached storage implementation. Contributed by Oleg Kalnichevski * HTTPCLIENT-1824, HTTPCLIENT-1868: Asynchronous HTTP cache storage API. Memcached backend implementation of async HTTP cache storage. Contributed by Oleg Kalnichevski * HTTPCLIENT-1885: Content compression exec interceptor generates incorrect 'Accept-Encoding' header value. Contributed by Oleg Kalnichevski Release 5.0-ALPHA3 ------------------- This is a major release that introduces support for the HTTP/2 protocol and event driven messaging APIs consistent for all supported HTTP protocol versions. HttpClient ships with two client implementations: * HttpClient Classic is based on the classic (blocking) I/O model; largely compatible with the 4.x APIs; supports HTTP/1.1 only. * HttpClient Async is based on NIO model; new event driven APIs consistent for all supported HTTP protocol versions; supports both HTTP/1.1 and HTTP/2. Notable new features in this release: * Asynchronous HttpClient implementations optimized for HTTP/2 multiplexed request execution. * Full support for HTTP caching by asynchronous HttpClient implementations including streaming message exchanages. Notable changes and features included in the 5.0 series are: * Support for the HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification documents (RFC 7540, RFC 7541.) Supported features: ** HPACK header compression ** Stream multiplexing (client and server) ** Flow control ** Response push ** Message trailers ** Expect-continue handshake ** Connection validation (ping) ** Application-layer protocol negotiation (ALPN) on Java 9.0.1+ ** TLS 1.2 security features * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification documents (RFC 7230, RFC 7231.) * Redesigned connection pool implementation with reduced pool lock contention. * Package name space changed to 'org.apache.hc.client5'. * Maven group id changed to 'org.apache.httpcomponents.client5'. * Apache Log4j2 logging APIs used for internal logging instead of Commons Logging APIs. HttpClient 5.0 releases can be co-located with earlier major versions on the same classpath due to the change in package names and Maven module coordinates. Please note that as of 5.0, HttpClient requires Java 1.7 or newer. Changelog: ------------------- * HttpAsyncClient implementations optimized for HTTP/2 multiplexed request execution. Contributed by Oleg Kalnichevski * Improved Ehcache and Memcached storage backends. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1827] Full support for HTTP caching by asynchronous HTTP clients. Contributed by Oleg Kalnichevski * Redesign of HTTP cache resource APIs. Contributed by Oleg Kalnichevski * Deprecated Content-Transfer-Encoding field in MIME body parts per RFC 7578, section 4.7. Contributed by Oleg Kalnichevski * [HTTPCLIENT-293] Implemented the percent encoding of the filename parameter of the Content-Disposition header. Contributed by Ioannis Sermetziadis * [HTTPCLIENT-1845]: Extract InputStreamFactory classes out of GzipDecompressingEntity and DeflateDecompressingEntity for reuse and to create less garbage. Contributed by Gary Gregory * [HTTPCLIENT-1858] Alleviate GC pressure due to wire logging. Contributed by Gary Gregory * Avoid fetching the cached entity twice on cache hit. Contributed by Leandro Nunes * [HTTPASYNC-124] Add doPrivileged blocks to async client and connection manager builders Contributed by Jay Modi Release 5.0-ALPHA2 ------------------- This is a major release that introduces support for the HTTP/2 protocol and event driven messaging APIs consistent for all supported HTTP protocol versions. HttpClient ships with two client implementations: * HttpClient Classic is based on the classic (blocking) I/O model; largely compatible with the 4.x APIs; supports HTTP/1.1 only. * HttpClient Async is based on NIO model; new event driven APIs consistent for all supported HTTP protocol versions; supports both HTTP/1.1 and HTTP/2. Notable changes and features included in the 5.0 series are: * Partial support for the HTTP/2 protocol and conformance to requirements and recommendations of the latest HTTP/2 protocol specification documents (RFC 7540, RFC 7541.) Supported features: ** HPACK header compression ** Stream multiplexing (client and server) ** Flow control ** Response push ** Message trailers ** Expect-continue handshake ** Connection validation (ping) ** Application-layer protocol negotiation (ALPN) on Java 1.9+ ** TLS 1.2 security features * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification documents (RFC 7230, RFC 7231.) * Redesigned connection pool implementation with reduced pool lock contention. * Package name space changed to 'org.apache.hc.client5'. * Maven group id changed to 'org.apache.httpcomponents.client5'. * Apache Log4j2 logging APIs used for internal logging instead of Commons Logging APIs. Features that are presently NOT supported: * HTTP/2 transport (classic) * HTTP tunneling (async) * Automatic response content decompression (async) * Caching (async) HttpClient 5.0 releases can be co-located with earlier major versions on the same classpath due to the change in package names and Maven module coordinates. Please note that as of 5.0, HttpClient requires Java 1.7 or newer. Please note that at this point 5.0 APIs are considered experimental and unstable and are expected to change in the coming releases without providing a migration path. Changelog: ------------------- * [HTTPCLIENT-1714] Add HttpClientBuilder.setDnsResolver(DnsResolver). Contributed by Alexis Thaveau * [HTTPCLIENT-1715] NTLMEngineImpl.Type1Message not thread safe but declared as a constant. Contributed by Olivier Lafontaine , Gary Gregory * [HTTPCLIENT-1716] redirect handling of unsafe methods defined by RFC 7231. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1717] Make fluent API .Content.Content(byte[], ContentType) public. Contributed by Cash Costello * [HTTPCLIENT-1730] added #setValidateAfterInactivity to HttpClientBuilder. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1752] Allow to configure the OSGI clients with relaxed SSL checks. Contributed by Simone Tripodi * [HTTPCLIENT-1748] Improved compatibility with system authentication API in applets. Contributed by Sebastien Caille * [HTTPCLIENT-1786] Port from Apache Commons Logging to Apache Log4j 2. Contributed by Gary Gregory * [HTTPCLIENT-1817] Add a "Trust All" TrustStrategy implementation. Contributed by Gary Gregory * [HTTPCLIENT-1836] DefaultHostnameVerifier#getSubjectAltNames(X509Certificate) throws java.lang.ClassCastException. Contributed by Gary Gregory , Ilian Iliev * [HTTPCLIENT-1691] HttpClient instance used internally by HC Fluent to take system properties into account by default. Contributed by Oleg Kalnichevski Release 5.0-ALPHA1 ------------------- This major release renders HttpClient API incompatible with the stable 4.x branch and upgrades HTTP/1.1 protocol conformance to the requirements and recommendations of the latest protocol specification. This release lays the foundation for transition to HTTP/2 as the primary transport protocol in the future releases. Notable changes and features included in the 5.0 series are: * Improved conformance to requirements and recommendations of the latest HTTP/1.1 protocol specification (RFC 7230, RFC 7231, RFC 7235) * Package name space changed to 'org.apache.hc.client5' * Maven group id changed to 'org.apache.httpcomponents.client5' * By default the maximum connections per route limit is set to 5 * By default connection request timeout and connect timeout are set to 3 minutes. HttpClient 5.0 releases can be co-located with earlier versions, meaning you can have both 5.x and 4.x on the classpath without experiencing jar hell. Please note that as of 5.0 HttpClient requires Java 1.7 or newer. Please note that at this point we consider 5.0 APIs experimental and unstable and expect them to change in the coming releases without providing a migration path. Changelog: ------------------- * [HTTPCLIENT-1575] route target port must be non negative Contributed by Oleg Kalnichevski * [HTTPCLIENT-1106] Use character arrays for passwords in Credentials objects Contributed by Oleg Kalnichevski * RFC 7235: redesign of HTTP authenticator and related classes Contributed by Oleg Kalnichevski * RFC 7231: parse capable of parsing multiple auth challenges Contributed by Oleg Kalnichevski * RFC 7231: DefaultServiceUnavailableRetryStrategy to take Retry-After header value into account if specified Contributed by Oleg Kalnichevski * RFC 7231: removed restriction on the use of relative URIs in Location header Contributed by Oleg Kalnichevski * RFC 7231: revised redirect handling Contributed by Oleg Kalnichevski * RFC 7231: do not generate header fields in TRACE requests containing sensitive data such as cookie and user credentials Contributed by Oleg Kalnichevski * RFC 7231: automatic retrial of idempotent methods Contributed by Oleg Kalnichevski * RFC 7230: increased the default max number of concurrent connection for the same route from 2 to 5 Contributed by Oleg Kalnichevski * Cache request line in HttpRequestWrapper Contributed by Dmitry Potapov * [HTTPCLIENT-1651] Add ability to disable content compression on a request basis Contributed by Oleg Kalnichevski * [HTTPCLIENT-1696]: Add convenience methods to fluent API class Request. Contributed by Gary Gregory Release 4.5.1 ------------------- HttpClient 4.5.1 (GA) is a maintenance release that fixes a number of minor defects found since 4.5. Please note that as of 4.4 HttpClient requires Java 1.6 or newer. Changelog: ------------------- * [HTTPCLIENT-1680] redirect of a POST request causes ClientProtocolException. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1673] org.apache.http.entity.mime.content.* missing from OSGi exports. Contributed by Benson Margulies * [HTTPCLIENT-1668] Fluent request incorrectly handles connect timeout setting. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1667] RequestBuilder does not take charset into account when creating UrlEncodedFormEntity. Contributed by Sergey Smith * [HTTPCLIENT-1655] HttpClient sends RST instead of FIN ACK sequence when using non-persistant connections. Contributed by Oleg Kalnichevski Release 4.5 ------------------- HttpClient 4.5 (GA) is a minor feature release that includes several incremental enhancements to the exisitng functionality such as support for private domains in the Mozilla Public Suffix List. Changelog: ------------------- * Reduced default validate after inactivity setting from 5 sec to 2 sec. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1649] Fixed serialization of auth schemes. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1645]: Fluent requests to inherit config parameters of the executor. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1640]: RFC6265 lax cookie policy fails to parse 'max-age' attribute. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1633]: RFC6265CookieSpecProvider compatibility level setting has no effect. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1613]: Support for private domains in Mozilla Public Suffix List. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1651]: Add ability to disable content compression on a request basis. Contributed by Michael Osipov * [HTTPCLIENT-1654]: Deprecate/remove RequestConfig#decompressionEnabled in favor of #contentCompressionEnabled. Contributed by Michael Osipov Release 4.4.1 ------------------- HttpClient 4.4.1 (GA) is a maintenance release that fixes a number of defects in new functionality introduced in version 4.4. Users of HttpClient 4.4 are encouraged to upgrade. Please note that as of 4.4 HttpClient requires Java 1.6 or newer. Changelog: ------------------- * Marked RFC 2109, RFC 2965, Netscape draft cookie specs as obsolete. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1633] RFC6265CookieSpecProvider compatibility level setting has no effect. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1628]: Auth cache can fail when domain name contains uppercase characters. Contributed by Dennis Ju * [HTTPCLIENT-1609] Stale connection check in PoolingHttpClientConnectionManager has no effect. Internal connection pool does not correctly implement connection validation. Contributed by Charles Lip Release 4.4 Final ------------------- This is the first stable (GA) release of HttpClient 4.4. Notable features and enhancements included in 4.4 series are: * 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 * More efficient stale connection checking: indiscriminate connection checking which results in approximately 20 to 50 ms overhead per request has been deprecated in favor of conditional connection state validation (persistent connections are to be re-validated only if a specified period inactivity has elapsed.) * 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. * Native Windows Negotiate and NTLM via SSPI through JNA: when running on Windows OS HttpClient configured to use native NTLM or SPNEGO authentication schemes can make use of platform specific functionality via JNA and current user credentials. This functionality is still considered experimental, known to have compatibility issues and subject to change without prior notice. Use at your discretion. This release also includes all fixes from the stable 4.3.x release branch. Please note that as of 4.4 HttpClient requires Java 1.6 or newer. Changelog: ------------------- * Support for the latest HTTP state management specification (RFC 6265). Contributed by Oleg Kalnichevski * [HTTPCLIENT-1515] Caching of responses to HEAD requests Contributed by Tyrone Cutajar and Francois-Xavier Bonnet * [HTTPCLIENT-1560] Native Windows auth improvements. Contributed by Michael Osipov * Update Apache Commons Logging version from 1.1.3 to 1.2. Contributed by Gary Gregory * Update Apache Commons Codec version from 1.6 to 1.9. Contributed by Gary Gregory * Update Ehcache version from 2.2.0 to 2.6.9. Contributed by Gary Gregory * Update Ehcache version from 2.2.0 to 2.6.9. Contributed by Gary Gregory * Update Spymemcached version from 2.6 to 2.11.4. Contributed by Gary Gregory * Update SLF4J version from 1.5.11 to 1.7.7. Contributed by Gary Gregory Release 4.4 BETA1 ------------------- This is the first BETA release of HttpClient 4.4. Notable features and enhancements included in 4.4 series are: * 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 * Native windows Negotiate/NTLM via JNA: when running on Windows OS HttpClient configured to use native NTLM or SPNEGO authentication schemes can make use of platform specific functionality via JNA and current user system credentials * More efficient stale connection checking: indiscriminate connection checking which results in approximately 20 to 50 ms overhead per request has been deprecated in favor of conditional connection state validation (persistent connections are to be re-validated only if a specified period inactivity has elapsed) * Authentication cache thread-safety: authentication caches used by HttpClient is now thread-safe and can be shared by multiple threads in order to re-use authentication state for subsequent requests This release also includes all fixes from the stable 4.3.x release branch. Please note that as of 4.4 HttpClient requires Java 1.6 or newer. Changelog: ------------------- * [HTTPCLIENT-1547] HttpClient OSGi bundle doesn't import the package "javax.naming". Contributed by Willem Jiang * [HTTPCLIENT-1541] Use correct (HTTP/hostname) service principal name for Windows native Negotiate/NTLM auth schemes. Contributed by Ka-Lok Fung * Improved compliance with RFC 2818: default hostname verifier to ignore the common name of the certificate subject if alternative subject names (dNSName or iPAddress) are present. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1540] Support delegated credentials (ISC_REQ_DELEGATE) by Native windows native Negotiate/NTLM auth schemes. Contributed by Ka-Lok Fung Release 4.4 ALPHA1 ------------------- This is the first ALPHA release of HttpClient 4.4. Notable features and enhancements included in the 4.4 branch are: * More efficient stale connection checking: indiscriminate connection checking which results in approximately 20 to 50 ms overhead per request has been deprecated in favor of conditional connection state validation (persistent connections are to be re-validated only if a specified period inactivity has elapsed) * Native windows Negotiate/NTLM via JNA: when running on Windows OS HttpClient configured to use native NTLM or SPNEGO authentication schemes can make use of platform specific functionality via JNA and current user system credentials * Authentication cache thread-safety: authentication caches used by HttpClient is now thread-safe and can be shared by multiple threads in order to re-use authentication state for subsequent requests This release also includes all fixes from the stable 4.3.x release branch. Please note that as of 4.4 HttpClient requires Java 1.6 or newer. Please note that new features included in this release are still considered experimental and their API may change in the future 4.4 alpha and beta releases. Changelog: ------------------- * [HTTPCLIENT-1493] Indiscriminate connection checking has been deprecated in favor of conditional connection state validation. Persistent connections are to be re-validated only after a defined period inactivity prior to being leased to the consumer. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1519] Use the original HttpHost instance passed as a parameter to HttpClient#execute when generating 'Host' request header. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1491] Enable provision of Service Principal Name in Windows native auth scheme. Contributed by Malcolm Smith * [HTTPCLIENT-1403] Pluggable content decoders. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1466] FileBodyPart#generateContentType() ignores custom ContentType values. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1461] fixed performance degradation in gzip encoded content processing introduced by HTTPCLIENT-1432. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1457] Incorrect handling of Windows (NT) credentials by SystemDefaultCredentialsProvider. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1456] Request retrial after status 503 causes ClientProtocolException. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1454] Make connection operator APIs public. Contributed by Tamas Cservenak * Update JUnit to version 4.11 from 4.9 Contributed by Gary Gregory Release 4.3.4 ------------------- HttpClient 4.3.4 (GA) is a maintenance release that improves performance in high concurrency scenarios. This version replaces dynamic proxies with custom proxy classes and eliminates thread contention in java.reflect.Proxy.newInstance() when leasing connections from the connection pool and processing response messages. Changelog: ------------------- * Replaced dynamic proxies with custom proxy classes to reduce thread contention. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1484] GzipCompressingEntity should not close the underlying output stream if the entity has not been fully written out due to an exception. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1474] Fixed broken entity enclosing requests in HC Fluent. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1470] CachingExec(ClientExecChain, HttpCache, CacheConfig, AsynchronousValidator) throws NPE if config is null Release 4.3.3 ------------------- HttpClient 4.3.3 (GA) is a bug fix release that fixes a regression introduced by the previous release causing a significant performance degradation in compressed content processing. Users of HttpClient 4.3 are encouraged to upgrade. Changelog: ------------------- * [HTTPCLIENT-1466] FileBodyPart#generateContentType() ignores custom ContentType values. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1453] Thread safety regression in PoolingHttpClientConnectionManager #closeExpiredConnections that can lead to ConcurrentModificationException. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1461] fixed performance degradation in compressed content processing introduced by HTTPCLIENT-1432. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1457] Incorrect handling of Windows (NT) credentials by SystemDefaultCredentialsProvider. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1456] Request retrial after status 503 causes ClientProtocolException. Contributed by Oleg Kalnichevski Release 4.3.2 ------------------- HttpClient 4.3.2 (GA) is a maintenance release that delivers a number of improvements as well as bug fixes for issues reported since 4.3.1 release. SNI support for Oracle JRE 1.7+ is being among the most notable improvements. Users of HttpClient 4.3 are encouraged to upgrade. Changelog: ------------------- * [HTTPCLIENT-1447] Clients created with HttpClients.createMinimal do not work with absolute URIs Contributed by Joseph Walton * [HTTPCLIENT-1446] NTLM proxy + BASIC target auth fails with 'Unexpected state: MSG_TYPE3_GENERATED'. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1443] HttpCache uses the physical host instead of the virtual host as a cache key. Contributed by Francois-Xavier Bonnet * [HTTPCLIENT-1442] Authentication header set by the user gets removed in case of proxy authentication (affects plan HTTP requests only). Contributed by Oleg Kalnichevski * [HTTPCLIENT-1441] Caching AsynchronousValidationRequest leaks connections. Contributed by Dominic Tootell * [HTTPCLIENT-1440] 'file' scheme in redirect location URI causes NPE. Contributed by James Leigh * [HTTPCLIENT-1437] Made Executor#execute thread safe. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1119] SNI support (Oracle Java 1.7+ only). Contributed by Bruno Harbulot * [HTTPCLIENT-1435] Fluent Executor ignores custom request properties. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1432] Lazy decompressing of HttpEntity#getContent() to avoid EOFException in case of an empty response with 'Content-Encoding: gzip' header. Contributed by Yihua Huang * [HTTPCLIENT-1431] (Regression) deprecated connection manager cannot be used with a custom LayeredSchemeSocketFactory. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1425] Fixed socket closed exception thrown by caching HttpClient when the origin server sends a long chunked response. Contributed by James Leigh * [HTTPCLIENT-1417] Fixed NPE in BrowserCompatSpec#formatCookies caused by version 1 cookies with null cookie value. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1416] Fixed NPE in CachingHttpClientBuilder#build(). Contributed by Oleg Kalnichevski Release 4.3.1 ------------------- HttpClient 4.3.1 (GA) is a bug fix release that addresses a number of issues reported since release 4.3. Users of HttpClient 4.3 are strongly encouraged to upgrade. Changelog ------------------- * [HTTPCLIENT-1410] Browser compatible hostname verifier no longer rejects *.co., *.gov., *.info., etc as invalid. Contributed by Oleg Kalnichevski * Ensure X509HostnameVerifier is never null. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1405] CONNECT HTTP/1.1 requests lack mandatory 'Host' header. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1402] Cache default User-Agent value. Contributed by yuexiaojun * [HTTPCLIENT-1398] Fixed invalid OSGi metadata caused by corrupted Maven bundle plugin metadata. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1399] Fixed NPE in RequestBuilder. Contributed by Oleg Kalnichevski Release 4.3 Final ------------------- This is the first stable (GA) release of HttpClient 4.3. The most notable enhancements included in this release are: * Support for Java 7 try-with-resources for resource management (connection release.) * Added fluent Builder classes for HttpEntity, HttpRequest, HttpClient and SSLContext instances. * Deprecation of preference and configuration API based on HttpParams interface in favor of constructor injection and plain configuration objects. * Reliance on object immutability instead of access synchronization for thread safety. Several old classes whose instances can be shared by multiple request exchanges have been replaced by immutable equivalents. * DefaultHttpClient, DecompressingHttpClient, CachingHttpClient and similar classes are deprecated in favor of builder classes that produce immutable HttpClient instances. * HttpClient builders now dynamically construct a request execution pipeline tailored specifically to the user configuration by physically excluding unnecessary protocol components. * There is now an option to construct a minimal HttpClient implementation that can only execute basic HTTP message exchanges without redirects, authentication, state management or proxy support. This feature might be of particular use in web crawler development. * There is now option to avoid strict URI syntax for request URIs by executing HTTP requests with an explicitly specified target host. HttpClient will no longer attempt to parse the request URI if it does not need to extract the target host from it. This release also includes all fixes from the stable 4.2.x release branch. Changelog ------------------- * [HTTPCLIENT-1371] Weak ETag Validation is Useful On PUT With If-Match Contributed by James Leigh * [HTTPCLIENT-1394] Support for Native windows Negotiate/NTLM via JNA Contributed by Ryan McKinley * [HTTPCLIENT-1384] Expose CacheInvalidator interface. Contributed by Nicolas Richeton * [HTTPCLIENT-1385] Fixed path normalization in CacheKeyGenerator Contributed by James Leigh * [HTTPCLIENT-1370] Response to non-GET requests should never be cached with the default ResponseCachingPolicy Contributed by James Leigh * [HTTPCLIENT-1373] OPTIONS and TRACE should not invalidate cache Contributed by James Leigh * [HTTPCLIENT-1383] HttpClient enters an infinite loop during NTLM authentication if the opposite endpoint keeps responding with a type 2 NTLM response after type 3 MTLM message has already been sent by the client. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1372] Refactor HttpMultipart, and add RFC6532 mode, so that headers in post are no longer constrained to ASCII values. Contributed by Karl Wright * [HTTPCLIENT-1377] User principal for non-NTLM authentication is incorrectly generated when using user credentials are specified as NTCredentials Contributed by Gary Gregory Release 4.3 BETA2 ------------------- This is the second BETA release of HttpClient 4.3. The most notable features and improvements in the 4.3 branch are: Support for Java 7 try-with-resources for resource management (connection release); fluent Builder classes for HttpEntity, HttpRequest and HttpClient instances, deprecation of preference and configuration API based on HttpParams interface in favor of constructor injection and plain configuration objects, reliance on object immutability instead of access synchronization for thread safety. This release also includes all fixes from the stable 4.2.x release branch. Changelog ------------------- * [HTTPCLIENT-1366] org.apache.http.client.utils.URLEncodedUtils should parse the semicolon as a query parameter separator. Contributed by Gary Gregory * [HTTPCLIENT-1365] NPE when ManagedHttpClientConnectionFactory.create(ConnectionConfig) is called with null. Contributed by Gary Gregory * [HTTPCLIENT-1362] Better error messages for connect timed out and connection refused exceptions. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1360] separate out DeflateInputStream as an independent class, so it can be used by others. Contributed by Karl Wright * [HTTPCLIENT-1359] repeated requests using the same context fail if they redirect. Contributed by James Leigh * [HTTPCLIENT-1354] do not quote algorithm parameter in DIGEST auth response. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1351] Added utility method to resolve final location from original request, target host and a list of redirects. Contributed by James Leigh * [HTTPCLIENT-1344] Userinfo credentials in URI should not default to preemptive BASIC authentication. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1345] Useinfo credentials ignored in redirect location header. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1294] HttpClient to rewrite host name of the redirect location URI in order to avoid circular redirect exception due to host name case mismatch. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1264] Add support for multiple levels of browser compatibility to BrowserCompatSpec and BrowserCompatSpecFactory. Include constructor argument for IE medium-security compatibility. Contributed by Karl Wright (kwright at apache.org) * [HTTPCLIENT-1349] SSLSocketFactory incorrectly identifies key passed with keystore as the keystore password. Contributed by David Graff * [HTTPCLIENT-1346] Ensure propagation of SSL handshake exceptions. Contributed by Pasi Eronen * [HTTPCLIENT-1343] SSLSocketFactory optional parameters for supported SSL protocols and cipher suites. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1238] Contribute Bundle Activator And Central Proxy Configuration. Contributed by Simone Tripodi * [HTTPCLIENT-1299] (regression) cache incorrectly disposes of the underlying cache resource when storing variant entry. Contributed by James Leigh * [HTTPCLIENT-1342] Redirects with underscore character in the location hostname cause "java.lang.IllegalArgumentException: Host name may not be null". Contributed by Oleg Kalnichevski Release 4.3 BETA1 ------------------- This is the first BETA release of HttpClient 4.3. The 4.3 branch enhances HttpClient in several key areas and includes several notable features and improvements: Support for Java 7 try-with-resources for resource management (connection release); fluent Builder classes for HttpEntity, HttpRequest and HttpClient instances, deprecation of preference and configuration API based on HttpParams interface in favor of constructor injection and plain configuration objects, reliance on object immutability instead of access synchronization for thread safety. This release also includes all fixes from the stable 4.2.x release branch. Changelog ------------------- * [HTTPCLIENT-1317] InetAddressUtils should handle IPv6 Addresses with Embedded IPv4 Addresses Contributed Sebastian Bazley . * [HTTPCLIENT-1320] Leverage javax.net.ssl.SSLSocketFactory#getDefault() to initialize SSL context based on system defaults instead of using an internal custom routine. Contributed by Abe Backus and Oleg Kalnichevski * [HTTPCLIENT-1316] Certificate verification rejects IPv6 addresses which are not String-equal. Contributed Sebastian Bazley . * [HTTPCLIENT-1307] Future based asynchronous request execution. Contributed by Jilles van Gurp * [HTTPCLIENT-1313] Fixed IllegalStateException in deprecated ThreadSafeClientConnManager. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1298] Add AsynchronousValidator in HttpClientBuilder's list of closeable objects. Contributed by Martin Meinhold Release 4.3 ALPHA1 ------------------- This is the first ALPHA release of HttpClient 4.3. The 4.3 branch enhances HttpClient in several key areas and includes several notable features and improvements: Support for Java 7 try-with-resources for resource management (connection release); fluent Builder classes for HttpEntity, HttpRequest and HttpClient instances, deprecation of preference and configuration API based on HttpParams interface in favor of constructor injection and plain configuration objects, reliance on object immutability instead of access synchronization for thread safety. We are kindly asking all upstream projects to review API changes and help us improve the APIs by providing feedback and sharing ideas on dev@hc.apache.org. This release also includes all fixes from the stable 4.2.x release branch. Please note that new features included in this release are still considered experimental and their API may change in the future 4.3 alpha and beta releases. Changelog ------------------- * [HTTPCLIENT-1250] Allow query string to be ignored when determining cacheability for HTTP 1.0 responses. Contributed by Don Brown * [HTTPCLIENT-1261] Make SystemDefaultHttpClient honor http.agent system property. Contributed by Oleg Kalnichevski * [HTTPCLIENT-900] Don't enforce URI syntax for messages with an explicit target host. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1190] HttpClient cache does not support "Vary: Cookie" Contributed by Oleg Kalnichevski * [HTTPCLIENT-1259] Calling #abort() on requests executed with DecompressingHttpClient has no effect. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1253] URIBuilder setParameter() method could exceed the HTTP header size. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1216] Added method to force clean thread-local used by DateUtils. Contributed by Oleg Kalnichevski Release 4.2.3 ------------------- HttpClient 4.2.3 (GA) is a bug fix release that addresses a number of issues reported since release 4.2.2. This release also includes a thoroughly reworked NTLM authentication engine which should result in a better compatibility with the newest Microsoft products. Users of HttpClient 4.x are advised to upgrade. Changelog ------------------- * [HTTPCLIENT-1296] NPE gets thrown if you combine a default host with a virtual host that has a -1 value for the port. Contributed by Karl Wright * [HTTPCLIENT-1290] 304 cached response never reused with If-modified-since conditional requests. Contributed by Francois-Xavier Bonnet * [HTTPCLIENT-1291] Absolute request URIs without an explicitly specified path are rewritten to have "/" path). Contributed by Oleg Kalnichevski * [HTTPCLIENT-1286] Request URI rewriting is inconsistent - URI fragments are not removed from absolute request URIs. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1284] HttpClient incorrectly generates Host header when physical connection route differs from the host name specified in the request URI. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1293] Kerberos and SPNego auth schemes use incorrect authorization header name when authenticating with a proxy. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1283] NTLM needs to use Locale-independent form of toUpperCase(). Contributed by Karl Wright * [HTTPCLIENT-1279] Target host responding with status 407 (proxy authentication required) causes an NPE. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1281] GzipDecompressingEntity does not release InputStream when an IOException occurs while reading the Gzip header Contributed by Francois-Xavier Bonnet * [HTTPCLIENT-1277] Caching client sends a 304 to an unconditional request. Contributed by Francois-Xavier Bonnet * [HTTPCLIENT-1278] Update NTLM documentation. Contributed by Karl Wright * SystemDefaultHttpClient misinterprets 'http.keepAlive' default value and disables connection persistence if the system property is not set. This causes connection based authentication schemes such as NTLM to fail. * [HTTPCLIENT-1276] cache update on a 304 response causes NPE. Contributed by Francois-Xavier Bonnet * [HTTPCLIENT-1273] DecompressingHttpClient does not automatically consume response content in case of an i/o, HTTP or runtime exception thrown by the decompressing protocol interceptor leading to a potential connection leak. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1268] NTLM engine refactor fix, to correct a buffer overrun, and get NTLMv2 flags right. Contributed by Karl Wright * [HTTPCLIENT-1266] NTLM engine refactoring and compatibility improvements. Contributed by Karl Wright * [HTTPCLIENT-1263] BrowserCompatSpec: attribute values containing spaces or special characters should be enclosed with quotes marks for version 1 cookies. Contributed by Francois-Xavier Bonnet * [HTTPCLIENT-1263] CachingHttpClient fails to release connections back to the connection manager for some type of HTTP response messages when used together with DecompressingHttpClient. Contributed by Francois-Xavier Bonnet * [HTTPCLIENT-1258] Fixed NullPointerException in NTLMEngineImpl caused by null NT domain attribute. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1254] Redirect with underscore in hostname causes ProtocolException. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1255] AbstractVerifier incorrectly parses certificate CN containing wildcard. Contributed by Oleg Kalnichevski Release 4.2.2 ------------------- HttpClient 4.2.2 (GA) is a bug fix release that addresses a number of issues reported since release 4.2.1. Users of HttpClient 4.2 are advised to upgrade. Changelog ------------------- * [HTTPCLIENT-1248] Default and lax redirect strategies should not convert requests redirected with 307 status to GET method. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1215] BasicAuthCache does not take default ports into consideration when looking up cached authentication details by HttpHost key. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1241] (regression) Preemptive BASIC authentication failure should be considered final and no further attempts to re-authenticate using the same credentials should be made. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1229] Fixed NPE in BasicClientConnectionManager that can be triggered by releasing connection after the connection manager has already been shut down. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1227] Date parsing in DateUtils made more efficient. Contributed by Patrick Linskey * [HTTPCLIENT-1224] (regression) NTLM auth not retried after a redirect over a non-persistent connection. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1223] Cache could be more aggressive on cache invalidations from Content-Location. Contributed by Jon Moore . Contributed by Jon Moore * [HTTPCLIENT-1217] AutoRetryHttpClient does not release connection used by the previous response when request is retried Contributed by Oleg Kalnichevski Release 4.2.1 ------------------- HttpClient 4.2.1 (GA) is a bug fix release that addresses a number of issues reported since release 4.2. Users of HttpClient 4.2 are advised to upgrade. Changelog ------------------- * [HTTPCLIENT-1209] Redirect URIs are now normalized. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1202] ResponseCachingPolicy should honor explicit cache-control directives for other status codes Contributed by Jon Moore * [HTTPCLIENT-1199] DecompressingHttpClient strips content from entity enclosing requests Contributed by Oleg Kalnichevski * [HTTPCLIENT-1198] HttpHost is not set in HttpContext in CachingHttpClient. Contributed by Jon Moore * [HTTPCLIENT-1200] DecompressingHttpClient fails to generate correct HttpHost context attribute. Contributed by Guillaume Castagnino * [HTTPCLIENT-1192] URIBuilder encodes query parameters twice. Contributed by Oleg Kalnichevski and Sebastian Bazley . * [HTTPCLIENT-1196] Fixed NPE in UrlEncodedFormEntity constructor thrown if charset is null. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1193] Fixed regression in the route tracking logic of the default connection manager causing cross-site redirect failures. Contributed by Oleg Kalnichevski Release 4.2 ------------------- This is the first stable (GA) release of HttpClient 4.2. The most notable enhancements included in this release are: * New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes only the most fundamental functions of HttpClient and is intended for relatively simple use cases that do not require the full flexibility of HttpClient. However, the fluent API almost fully relieves the users from having to deal with connection management and resource deallocation. * Redesigned and rewritten connection management code. * Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges and retry authentication with a fall-back scheme in case the primary one fails. This can be important for compatibility with Microsoft products that are often configured to use SPNEGO/Kerberos as the preferred authentication scheme. Changelog ------------------- * [HTTPCLIENT-1187] If a revalidation response is deemed too old CachingHttpClient fails to consume its content resulting in a connection leak. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1186] State of newly created connections in the connection pool is not always correctly updated potentially allowing those connections to be leased to users with a different security context. Contributed by Ralf Poehlmann * [HTTPCLIENT-1179] Upgraded Commons Codec dependency to version 1.6 Contributed by Oleg Kalnichevski * [HTTPCLIENT-1177] always remove fragments from request URIs Contributed by Oleg Kalnichevski Incompatible changes -------------------- [Compared to release version 4.1.3] The following fields have been deprecated for some time now and have been deleted: org.apache.http.client.params.ClientPNames#CONNECTION_MANAGER_FACTORY org.apache.http.impl.cookie.BrowserCompatSpec#DATE_PATTERNS The following methods have been deprecated for some time now and have been deleted: org.apache.http.client.params.ClientParamBean#setConnectionManagerFactory(org.apache.http.conn.ClientConnectionManagerFactory) org.apache.http.client.protocol.ClientContextConfigurer#setAuthSchemePref(java.util.List) org.apache.http.entity.mime.content.FileBody#writeTo(java.io.OutputStream, int) org.apache.http.entity.mime.content.InputStreamBody#writeTo(java.io.OutputStream, int) org.apache.http.entity.mime.content.StringBody#writeTo(java.io.OutputStream, int) The following classes have been deprecated for some while now and have been deleted: org.apache.http.impl.conn.tsccm.RefQueueHandler org.apache.http.impl.conn.tsccm.AbstractConnPool no longer implements interface org.apache.http.impl.conn.tsccm.RefQueueHandler org.apache.http.impl.conn.tsccm.ConnPoolByRoute no longer implements interface org.apache.http.impl.conn.tsccm.RefQueueHandler org.apache.http.impl.conn.tsccm.RefQueueWorker Release 4.2 BETA1 ------------------- This is the first BETA release of HttpClient 4.2. This release completes development of several notable enhancements in HttpClient: * New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes only the most fundamental functions of HttpClient and is intended for relatively simple use cases that do not require the full flexibility of HttpClient. However, the fluent API almost fully relieves the users from having to deal with connection management and resource deallocation. * Redesigned and rewritten connection management code. As of release 4.2 HttpClient will be using pooling connection manager per default. * Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges and retry authentication with a fall-back scheme in case the primary one fails. This can be important for compatibility with Microsoft products that are often configured to use SPNEGO/Kerberos as the preferred authentication scheme. Changelog ------------------- * [HTTPCLIENT-1164] Compressed entities are not being cached properly. Contributed by Jon Moore . * [HTTPCLIENT-1154] MemcachedHttpCacheStorage should allow client to specify custom prefix string for keys. Contributed by Jon Moore . * [HTTPCLIENT-1153] MemcachedHttpCacheStorage uses URL as cache key; shouldn't due to fixed maximum-length memcached keys. Contributed by Jon Moore . * [HTTPCLIENT-1157] MemcachedHttpCacheStroage should throw IOExceptions instead of RuntimeExceptions. Contributed by James Miller . * [HTTPCLIENT-1152] MemcachedHttpCacheStorage should verify class of returned object before casting. Contributed by Rajika Kumarasiri . * [HTTPCLIENT-1155] CachingHttpClient fails to ensure that the response content gets fully consumed when using a ResponseHandler, which can potentially lead to connection leaks. Contributed by James Miller * [HTTPCLIENT-1147] When HttpClient-Cache cannot open cache file, should act like miss. Contributed by Joe Campbell * [HTTPCLIENT-1137] Values for the Via header are cached and reused by httpclient-cache. Contributed by Alin Vasile * [HTTPCLIENT-1142] Infinite loop on NTLM authentication failure. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1143] CachingHttpClient leaks connections with stale-if-error. Contributed by James Miller Release 4.2 ALPHA1 ------------------- This is the first ALPHA release of HttpClient 4.2. The 4.2 branch enhances HttpClient in several key areas and includes several notable features and improvements: * New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes only the most fundamental functions of HttpClient and is intended for relatively simple use cases that do not require the full flexibility of HttpClient. However, the fluent API almost fully relieves the users from having to deal with connection management and resource deallocation. * Redesigned and rewritten connection management code. As of release 4.2 HttpClient will be using pooling connection manager per default. * Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges and retry authentication with a fall-back scheme in case the primary one fails. This can be important for compatibility with Microsoft products that are often configured to use SPNEGO/Kerberos as the preferred authentication scheme. Please note that new features included in this release are still considered experimental and their API may change in the future ALPHA releases. Changelog ------------------- * [HTTPCLIENT-1128] SystemDefaultHttpClient (HttpClient implementation initialized using system properties). Contributed by Oleg Kalnichevski * [HTTPCLIENT-1135] RandomAccessFile mode 'w' used by HttpClientCache is not valid. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1131] HttpClient to authenticate preemptively using BASIC scheme if a userinfo attribute is specified in the request URI. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1134] make BasicResponseHandler consume response content in case of an unsuccessful result (status code >= 300). Contributed by Oleg Kalnichevski * [HTTPCLIENT-1132] ProxyClient implementation. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1127] fixed dead-lock between SingleClientConnManager and AbstractPooledConnAdapter. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1107] Auth framework redesign. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1116] ResponseCachingPolicy uses integers for sizes Contributed by Greg Bowyer * [HTTPCLIENT-1123] Support for pluggable DNS resolvers. Contributed by Alin Vasile * [HTTPCLIENT-1120] DefaultHttpRequestRetryHandler#retryRequest should not retry aborted requests. Contributed by Alin Vasile * Support for auth-int qop (quality of protection) option in Digest auth scheme. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1076] Fluent facade API (Google summer of code 2011 project). Contributed by Xu Lilu * UriBuilder implementation. Contributed by Xu Lilu * Redesign of connection management classes based on new pooling components from HttpCore. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1111] Added #prepareSocket method to SSLSocketFactory. Contributed by Pasi Eronen * Added #reset() and #releaseConnection() methods to HttpRequestBase. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1105] AutoRetryHttpClient: built-in way to do auto-retry for certain status codes. Contributed by Dan Checkoway * [HTTPCLIENT-1094] Digest auth scheme refactoring. Contributed by Oleg Kalnichevski * Lax implementation of RedirectStrategy. Contributed by Bartosz Firyn * [HTTPCLIENT-1044] HttpRequestRetryHandler implementation compliant with the definition of idempotent methods given in the RFC 2616. Contributed by Oleg Kalnichevski Release 4.1.2 ------------------- The HttpClient 4.1.2 is a bug fix release that addresses a number of non-critical issues reported since release 4.1.1. * [HTTPCLIENT-1100] Missing Content-Length header makes cached entry invalid Contributed by Bart Robeyns * [HTTPCLIENT-1098] Avoid expensive reverse DNS lookup on connect timeout exception. Contributed by Thomas Boettcher * [HTTPCLIENT-1097] BrowserCompatHostnameVerifier and StrictHostnameVerifier should handle wildcards in SSL certificates better. Contributed by Sebastian Bazley * [HTTPCLIENT-1092] If ClientPNames.VIRTUAL_HOST does not provide the port, derive it from the current request. Contributed by Sebastian Bazley * [HTTPCLIENT-1087] NTLM proxy authentication fails on retry if the underlying connection is closed as a result of a target authentication failure. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1079] Fixed Kerberos cross-realm support Contributed by Michael Osipov <1983-01-06 at gmx.net> * [HTTPCLIENT-1078] Decompressing entities (DeflateDecompressingEntity, GzipDecompressingEntity) do not close content stream in #writeTo() method. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1075] Decompressing entities (DeflateDecompressingEntity, GzipDecompressingEntity) do not correctly handle content streaming. Contributed by James Abley * [HTTPCLIENT-1051] Avoid reverse DNS lookups when opening SSL connections by IP address. Contributed by Oleg Kalnichevski Release 4.1.1 ------------------- HttpClient v4.1.1 is a bug fix release that addresses a number of issues reported since release 4.1, including one critical security issue (HTTPCLIENT-1061). All users of HttpClient 4.0.x and 4.1 are strongly encouraged to upgrade. * [HTTPCLIENT-1069] HttpHostConnectException not correctly retried for direct and non-tunnelled proxy connections. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1066] Changed the way URIUtils#rewriteURI handles multiple consecutive slashes in the URI path component: multiple leading slashes will be replaced by one slash in order to avoid confusion with the authority component. The remaining content of the path will not be modified. (also see HTTPCLIENT-929). Contributed by Oleg Kalnichevski * [HTTPCLIENT-1061] Fixed critical bug causing Proxy-Authorization header to be sent to the target host when tunneling requests through a proxy server that requires authentication. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1056] Fixed bug causing the RequestAuthCache protocol interceptor to generate an invalid AuthScope instance when looking up user credentials for preemptive authentication. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1053] Fixed the way DigestScheme generates nonce-count values. Contributed by Oleg Kalnichevski Release 4.1 ------------------- The HttpClient 4.1 release builds upon the stable foundation laid by HttpClient 4.0 and adds several functional improvements and popular features. * Response caching conditionally compliant with HTTP/1.1 specification (full compliance with MUST requirements, partial compliance with SHOULD requirements) * Full support for NTLMv1, NTLMv2, and NTLM2 Session authentication. The NTLM protocol code was kindly contributed by the Lucene Connector Framework project. * Support for SPNEGO/Kerberos authentication. * Persistence of authentication data between request executions within the same execution context. * Support for preemptive authentication for BASIC and DIGEST schemes. * Support for transparent content encoding. Please note transparent content encoding is not enabled per default in order to avoid conflicts with already existing custom content encoding solutions. * Mechanism to bypass the standard certificate trust verification (useful when dealing with self-signed certificates). * Simplified configuration for connection managers. * Transparent support for host multihoming. IMPORTANT: please note that the HttpClient 3.x branch is now officially END OF LIFE and is no longer maintained and supported by the Apache HttpComponents project. Changelog ------------------- * The public API for the caching module had a minor change between 4.1-beta and 4.1-GA to the HttpCacheEntry class - the deprecated public Set getVariantURIs() method and constructor public HttpCacheEntry(Date requestDate, Date responseDate, StatusLine statusLine, Header[] responseHeaders, Resource resource, Set variants) were both removed. This will not affect you unless you are implementing new storage backends that use the deprecated code and/or are implementing custom serializers for cache entries. * Changed Browser-Compatibility and Best-Match cookie policies to emulate the behaviour of FireFox more closely when parsing Netscape style cookies. Comma will no longer be treated as a header element separator if Set-Cookie does not contain a Version attribute mandated by the RFC2109 / RFC 2965 cookie specifications. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1036] StringBody has incorrect default for characterset. (Default changed to US-ASCII) Contributed by Sebastian Bazley * [HTTPCLIENT-975] Support stale-if-error and stale-while-revalidate extension directive (RFC5861). Contributed by Mohammed Azeem Uddin , Michajlo Matijkiw , and Matthew Hawthorne . * [HTTPCLIENT-1033] HttpRoute.equals(Object o) is quite inefficient, as it does not take full advantage of shortcut logic. Contributed by Sebastian Bazley * [HTTPCLIENT-1030] Implement "ignoreCookies" CookieSpec Contributed by Sebastian Bazley Release 4.1 BETA1 ------------------- HttpClient 4.1 BETA1 finalizes the 4.1 API and brings a number of major improvements to the HTTP caching module. This release also adds full support for NTLMv1, NTLMv2, and NTLM2 Session authentication. The NTLM protocol code was kindly contributed by the Lucene Connector Framework project. Changelog ------------------- * [HTTPCLIENT-1015] Support only-if-cached directive. Contributed by Michajlo Matijkiw * [HTTPCLIENT-990] Allow heuristic freshness caching. Contributed by Michajlo Matijkiw * [HTTPCLIENT-919] Support for NTLMv1, NTLMv2, and NTLM2 Session authentication. Contributed by Karl Wright * [HTTPCLIENT-1008] Send all variants' ETags on "variant miss". Contributed by Michajlo Matijkiw and Mohammed Azeem Uddin * [HTTPCLIENT-1011] Handling of IOExceptions thrown by cache components. Contributed by Jonathan Moore * [HTTPCLIENT-1003] Handle conditional requests in cache. Contributed by Michajlo Matijkiw and Mohammed Azeem Uddin * [HTTPCLIENT-1002] Stale connection check fails if wire logging is on. Contributed by Oleg Kalnichevski * [HTTPCLIENT-1000] Maximum connection lifetimes settings for ThreadSafeClientConnManager. Contributed by Michajlo Matijkiw * [HTTPCLIENT-960] HttpMultipart doesn't generate Content-Type header for binary parts in BROWSER_COMPATIBLE mode. Contributed by Oleg Kalnichevski * [HTTPCLIENT-998] Cache should use both Last-Modified and ETag for validations when available. Contributed by Jonathan Moore * [HTTPCLIENT-997] Cache module should handle out-of-order validations properly and unconditionally refresh. Contributed by Jonathan Moore * [HTTPCLIENT-994] Cache does not allow client to override origin-specified freshness using max-stale. Contributed by Jonathan Moore * [HTTPCLIENT-995] Cache returns cached responses even if validators not consistent with all conditional headers. Contributed by Jonathan Moore * [HTTPCLIENT-977] Memcached implementation for HttpCache. Contributed by Mohammed Azeem Uddin * [HTTPCLIENT-992] cache should not generate stale responses to requests explicitly requesting first-hand or fresh ones. Contributed by Jonathan Moore * [HTTPCLIENT-991] cache module produces improperly formatted Warning header when revalidation fails. Contributed by Jonathan Moore * [HTTPCLIENT-989] DefaultHttpRequestRetryHandler no longer retries non-idempotent http methods if NoHttpResponseException is thrown. Contributed by Oleg Kalnichevski * [HTTPCLIENT-988] Cache module should strip 'Content-Encoding: identity' from responses Contributed by Jonathan Moore * [HTTPCLIENT-987] cache module does not recognize equivalent URIs. Contributed by Jonathan Moore * [HTTPCLIENT-986] cache module does not completely handle upstream Warning headers correctly Contributed by Jonathan Moore * [HTTPCLIENT-985] cache module should populate Via header to capture upstream and downstream protocols Contributed by Jonathan Moore * [HTTPCLIENT-984] Additional conditional compliance tests for the caching module for Content-Encoding, Content-Location, Date, Expires, Server, Transfer-Encoding, and Vary headers. Contributed by Jonathan Moore * [HTTPCLIENT-978] HTTP cache update exception handling Contributed by Michajlo Matijkiw * [HTTPCLIENT-981] CachingHttpClient returns a 411 respones when executing a POST (HttpPost) request. Contributed by Joe Campbell * [HTTPCLIENT-980] CachingHttpClient returns a 503 response when the backend HttpClient produces an IOException. Contributed by Jonathan Moore * [HTTPCLIENT-978] Ehcache based HTTP cache implementation Contributed by Michajlo Matijkiw * [HTTPCLIENT-967] support for non-shared (private) caches Contributed by Jonathan Moore * [HTTPCLIENT-969] BasicCookieStore#getCookies() to return a copy of Cookie list Contributed by David Smiley * [HTTPCLIENT-965] Fixed problem with cache not honoring must-revalidate or proxy-revalidate Cache-Control directives. Contributed by Jonathan Moore * [HTTPCLIENT-964] 'no-cache' directives with field names are no longer transmitted downstream. Contributed by Jonathan Moore * [HTTPCLIENT-963] Fixed handling of 'Cache-Control: no-store' on requests. Contributed by Jonathan Moore * [HTTPCLIENT-962] Fixed handling of Authorization headers in shared cache mode. Contributed by Jonathan Moore * [HTTPCLIENT-961] Not all applicable URIs are invalidated on PUT/POST/DELETEs that pass through client cache. Contributed by Jonathan Moore * [HTTPCLIENT-958] Client cache no longer allows incomplete responses to be passed on to the client. Contributed by Jonathan Moore * [HTTPCLIENT-951] Non-repeatable entity enclosing requests are not correctly retried when 'expect-continue' handshake is active. Contributed by Oleg Kalnichevski * [HTTPCLIENT-948] In rare circumstances the idle connection handling code can leave closed connections in a inconsistent state. Contributed by Oleg Kalnichevski * [HTTPCLIENT-953] IllegalStateException thrown by RouteSpecificPool. Contributed by Guillaume * [HTTPCLIENT-952] Trust store parameter is ignored by SSLSocketFactory (affects version 4.1-alpha2 only) Contributed by Oleg Kalnichevski * [HTTPCLIENT-937] CacheEntry made immutable; now uses immutable HttpEntity to store cached content. Contributed by David Mays and Oleg Kalnichevski Release 4.1 ALPHA2 ------------------- HttpClient 4.1 ALPHA2 fixes a number of non-severe bugs discovered since the last release and introduces support for two frequently requested features: * HTTP/1.1 response caching * transparent support for host multihoming * a mechanism to bypass the standard certificate trust verification (useful when dealing with self-signed certificates) Compatibility notes ------------------- (1) Please note the HTTP caching module is still considered experimental and its API may change significantly in the future releases. (2) This release eliminates Mime4J as a dependency for the HttpMime module. HttpMime is no longer binary compatible with the previous releases. Full API and binary compatibility between minor versions of HttpMime will be maintained as of 4.1 GA release. Changelog ------------------- * [HTTPCLIENT-936] Fixed bug causing NPE or an infinite loop in the authentication code in case of a SPNEGO authentication failure. Contributed by Oleg Kalnichevski * [HTTPCLIENT-427] HTTP caching support Contributed by Joe Campbell, David Cleaver, David Mays, Jon Moore, Brad Spenla * Dropped dependency on Mime4j for HttpMime. Contributed by Oleg Kalnichevski * Extended SSLSocketFactory with a mechanism to bypass the standard certificate trust verification (primarily to simplify dealing with self-signed certificates) Contributed by Oleg Kalnichevski * [HTTPCLIENT-898] Improved support for host multihoming Contributed by Oleg Kalnichevski * [HTTPCLIENT-916] UsernamePasswordCredentials, NTUserPrincipal, BasicClientCookie, BasicClientCookie2 and BasicCookieStore made Serializable. Contributed by Oleg Kalnichevski * [HTTPCLIENT-914] Upgraded Commons Codec dependency to version 1.4 Contributed by Oleg Kalnichevski * [HTTPCLIENT-903] Use ConcurrentHashMap instead of [Linked]HashMap for thread-safety. Improve performance of AuthSchemeRegistry, CookieSpecRegistry and SchemeRegistry classes. Contributed by Sebastian Bazley * [HTTPCLIENT-902] HttpRequestRetryHandler not called on I/O exceptions thrown when opening a new connection. Contributed by Olivier Lamy and Oleg Kalnichevski Release 4.1 ALPHA1 ------------------- HttpClient 4.1 ALPHA1 builds on the stable 4.0 release and adds several functionality improvements and new features. * Simplified configuration of connection managers. * Persistence of authentication data between request executions within the same execution context. * Support for SPNEGO/Kerberos authentication scheme * Support for transparent content encoding. Please note transparent content encoding is not enabled per default in order to avoid conflicts with already existing custom content encoding solutions. * 5 to 10% performance increase due to elimination of unnecessary Log object lookups by short-lived components. Please note all methods and classes added in this release and marked as 4.1 are API unstable and can change in the future 4.1 ALPHA releases. Changelog ------------------- * [HTTPCLIENT-889] 'expect: continue' handshake disabled per default. Contributed by Oleg Kalnichevski * [HTTPCLIENT-862] Extended client's redirect handling interface to allow control of the content of the redirect. Contributed by Oleg Kalnichevski * [HTTPCLIENT-872] HttpClient can now persist authentication data between request executions as long as they share the same execution context. It has also become much easier to make HttpClient authenticate preemptively by pre-populating authentication data cache. Contributed by Oleg Kalnichevski * [HTTPCLIENT-883] SO_TIMEOUT is not reset on persistent (re-used) connections. Contributed by Oleg Kalnichevski * [HTTPCLIENT-832] Distinguish cookie format errors from violations of restrictions imposed by a cookie specification. In the latter case CookieRestrictionViolationException will be thrown. Contributed by Oleg Kalnichevski * [HTTPCLIENT-523] Support for SPNEGO authentication scheme. Contributed by Matthew Stevenson * Simplified configuration of connection managers. Total connection maximum and maximum connection per route limits can be set using methods of the class instead of HTTP parameters. Contributed by Oleg Kalnichevski * Added parameters to define the order of preference for supported auth schemes for target host and proxy authentication. Contributed by Oleg Kalnichevski * [HTTPCLIENT-875] DefaultClientConnectionOperator#openConnection doesn't update the connection state if the connection socket changed after the call to SocketFactory#connectSocket(). Contributed by Oleg Kalnichevski * [HTTPCLIENT-834] Transparent content encoding support. Contributed by James Abley Release 4.0.1 ------------------- This is a bug fix release that addresses a number of issues discovered since the previous stable release. None of the fixed bugs is considered critical. Most notably this release eliminates eliminates dependency on JCIP annotations. This release is also expected to improve performance by 5 to 10% due to elimination of unnecessary Log object lookups by short-lived components. Changelog ------------------- * [HTTPCLIENT-895] Eliminated Log lookups in short lived objects impairing performance. Contributed by Oleg Kalnichevski * [HTTPCLIENT-885] URLEncodedUtils now correctly parses form-url-encoded entities that specify a charset. Contributed by Oleg Kalnichevski * [HTTPCLIENT-884] UrlEncodedFormEntity now sets charset on the Content-Type header. Contributed by Jared Jacobs * [HTTPCLIENT-883] SO_TIMEOUT is not reset on persistent (re-used) connections. Contributed by Oleg Kalnichevski * [HTTPCLIENT-882] Auth state is now correctly updated if a successful NTLM authentication results in a redirect. This is a minor bug as HttpClient manages to recover from the problem automatically. Contributed by Oleg Kalnichevski * [HTTPCLIENT-881] Fixed race condition in AbstractClientConnAdapter that makes it possible for an aborted connection to be returned to the pool. Contributed by Tim Boemker and Oleg Kalnichevski * [HTTPCLIENT-866] Removed dependency on jcip-annotations.jar. Contributed by Oleg Kalnichevski and Sebastian Bazley Release 4.0 ------------------- HttpClient 4.0 represents a complete, ground-up redesign and almost a complete rewrite of the HttpClient 3.x codeline. This release finally addresses several design flaws that existed since the 1.0 release and could not be fixed without a major code overhaul and breaking API compatibility. Architectural changes --------------------- * Redesign of the HttpClient internals addressing all known major architectural shortcomings of the 3.x codeline. * Cleaner, more flexible and expressive API. * More modular structure. * Better performance and smaller memory footprint due to a more efficient HTTP transport based on HttpCore. * Implementation of cross-cutting HTTP protocol aspects through protocol interceptors. * Improved connection management, better handling of persistent connections, support for stateful connections * Pluggable redirect and authentication handlers. * Improved support for sending requests via a proxy or a chain of proxies * More flexible SSL context customization * Reduced intermediate garbage in the process of generating HTTP requests and parsing HTTP responses Important notes ------------------- * Future releases of HttpMime module may be binary incompatible with this release due to possible API changes in Apache Mime4J. Apache Mime4J is still being actively developed and its API is considered unstable. * HttpClient 4.0 is not fully binary compatible with 4.0 BETA1 release. Some protected variables in connection management class have been made final in order to help ensure their thread safety: org.apache.http.conn.BasicEofSensorWatcher#attemptReuse org.apache.http.conn.BasicEofSensorWatcher#managedConn org.apache.http.impl.conn.DefaultClientConnectionOperator#schemeRegistry org.apache.http.impl.conn.DefaultHttpRoutePlanner#schemeRegistry org.apache.http.impl.conn.ProxySelectorRoutePlanner#schemeRegistry org.apache.http.impl.conn.SingleClientConnManager#alwaysShutDown org.apache.http.impl.conn.SingleClientConnManager#connOperator org.apache.http.impl.conn.SingleClientConnManager#schemeRegistry org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#connOperator org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#schemeRegistry Bug fixes since 4.0 BETA2 release ------------------- * [HTTPCLIENT-861] URIUtils#resolve is now compatible with all examples given in RFC 3986. Contributed by Johannes Koch * [HTTPCLIENT-860] HttpClient no longer converts redirects of PUT/POST to GET for status codes 301, 302, 307, as required by the HTTP spec. Contributed by Oleg Kalnichevski * [HTTPCLIENT-859] CookieIdentityComparator now takes path attribute into consideration when comparing cookies. Contributed by Oleg Kalnichevski * HttpClient will no longer send expired cookies back to the origin server. Contributed by Oleg Kalnichevski * [HTTPCLIENT-856] Proxy NTLM authentication no longer fails on a redirect to a different host. Contributed by Oleg Kalnichevski * [HTTPCLIENT-841] Removed automatic connection release using garbage collection due to a memory leak. Contributed by Oleg Kalnichevski * [HTTPCLIENT-853] Fixed bug causing invalid cookie origin port to be selected when the target is accessed on the default port and the connection is established via a proxy. Contributed by Oleg Kalnichevski * [HTTPCLIENT-852] Fixed bug causing automatically retried redirects fail with CircularRedirectException. Contributed by Oleg Kalnichevski * Fixed problem with the default HTTP response parser failing to handle garbage preceding a valid HTTP response. Contributed by Oleg Kalnichevski * NonRepeatableRequestExceptions now include the cause that the original request failed. Contributed by Sam Berlin * [HTTPCLIENT-837] Fixed problem with the wire log skipping zero byte values if read one byte at a time. Contributed by Kirill Safonov * [HTTPCLIENT-823] 'http.conn-manager.max-total' parameter can be adjusted dynamically. However, the size of existing connection pools per route, once allocated, will not be adjusted. Contributed by Oleg Kalnichevski * [HTTPCLIENT-822] Default socket factories to rethrow SocketTimeoutException as ConnectTimeoutException in case of connect failure due to a time out. Contributed by Oleg Kalnichevski * [HTTPCLIENT-813] Fixed default port resolution. Invalid ports no longer get replaced with the default port value. Contributed by Oleg Kalnichevski Release 4.0 beta 2 ------------------- BETA2 is a maintenance release, which addresses a number of issues discovered since the previous release. The only significant new feature is an addition of an OSGi compliant bundle combining HttpClient and HttpMime jars. All upstream projects are strongly encouraged to upgrade. * Fixed NPE in DefaultRequestDirector thrown when retrying a failed request over a proxied connection. Contributed by Oleg Kalnichevski * [HTTPCLIENT-803] Fixed bug in SSL host verifier implementations causing the SSL certificate to be rejected as invalid if the connection is established using an IP address. Contributed by Oleg Kalnichevski * [HTTPCLIENT-806] DefaultHttpMethodRetryHandler will no longer retry on ConnectExceptions. Contributed by Oleg Kalnichevski * DigestScheme can use an arbitrary digest algorithm requested by the target server (such as SHA) as long as this algorithm is supported by the Java runtime. Contributed by Oleg Kalnichevski * Fixed parsing and validation of RFC2109 compliant Set-Cookie headers by the Best-Match cookie spec. Contributed by Oleg Kalnichevski * Fixed bug that can cause a managed connection to be returned from the pool in an inconsistent state. Contributed by Oleg Kalnichevski 4.0 Beta 1 ------------------- BETA1 release brings yet another round of API enhancements and improvements in the area of connection management. Among the most notable ones is the capability to handle stateful connections such as persistent NTLM connections and private key authenticated SSL connections. This is the first API stable release of HttpClient 4.0. All further releases in the 4.0 code line will maintain API compatibility with this release. There has been a number of important bug fixes since ALPHA4. All upstream projects are encouraged to upgrade to the latest release. Please note HttpClient currently provides only limited support for NTLM authentication. For details please see NTLM_SUPPORT.txt. ------------------- Changelog: ------------------- * [HTTPCLIENT-790] Protocol interceptors are now correctly invoked when executing CONNECT methods. Contributed by Oleg Kalnichevski * [HTTPCLIENT-668] Do not use static loggers. Contributed by Oleg Kalnichevski * [HTTPCLIENT-781] Respect Keep-Alive header's timeout value. Contributed by Sam Berlin * [HTTPCLIENT-779] Top-level classes (HttpClient, and HttpGet, HttpPut and similar HttpMethods) throw fewer checked exceptions. Contributed by Sam Berlin * HttpClient will throw an exception if an attempt is made to retry a request with a non-repeatable request entity. Contributed by Oleg Kalnichevski * Fixed request re-generation logic when retrying a failed request. Auto-generated headers will no accumulate. Contributed by Oleg Kalnichevski * [HTTPCLIENT-424] Preemptive authentication no longer limited to BASIC scheme only. HttpClient can be customized to authenticate preemptively with DIGEST scheme. Contributed by Oleg Kalnichevski * [HTTPCLIENT-670] Pluggable hostname resolver. Contributed by Oleg Kalnichevski * [HTTPCLIENT-719] Clone support for HTTP request and cookie objects. Contributed by Oleg Kalnichevski * [HTTPCLIENT-776] Fixed concurrency issues with AbstractPoolEntry. Contributed by Sam Berlin * Resolved a long standing problem with HttpClient not taking into account the user context when pooling / re-using connections. HttpClient now correctly handles stateful / user specific connections such as persistent NTLM connections and SSL connections with client side authentication. Contributed by Oleg Kalnichevski * [HTTPCLIENT-773] Improved handling of the 'expires' attribute by the 'Best Match' cookie spec. Contributed by Oleg Kalnichevski * Partial NTLM support (requires an external NTLM engine). For details see NTLM_SUPPORT.txt Contributed by Oleg Kalnichevski * Redesigned local execution context management. Contributed by Oleg Kalnichevski -------------------------------------- Release 4.0 Alpha 4 ------------------- ALPHA4 marks the completion of the overhaul of the connection management code in HttpClient. All known shortcomings of the old HttpClient 3.x connection management API have been addressed. NTLM authentication remains the only missing major feature in the new codeline that prevents us from moving awards the API freeze. There has been a number of important bug fixes since ALPHA3. All upstream projects are encouraged to upgrade to the latest release. ------------------- HttpClient 3.x features that have NOT yet been ported: ------------------- * NTLM authentication scheme ------------------- Changelog: ------------------- * [HTTPCLIENT-765] String.toLowerCase() / toUpperCase() should specify Locale.ENGLISH Contributed by Sebastian Bazley * [HTTPCLIENT-769] Do not pool connection marked non-reusable. Contributed by Oleg Kalnichevski * [HTTPCLIENT-763] Fixed problem with AbstractClientConnAdapter#abortConnection() not releasing the connection if called from the main execution thread while there is no blocking I/O operation. Contributed by Oleg Kalnichevski * [HTTPCLIENT-652] Added optional state attribute to managed client connections. This enables connection managers to correctly handle stateful connections. Contributed by Oleg Kalnichevski * [HTTPCLIENT-673] Revised max connections per route configuration Contributed by Oleg Kalnichevski * [HTTPCLIENT-753] Class Scheme and related classes moved to a separate package Contributed by Oleg Kalnichevski * [HTTPCLIENT-757] Improved request wrapping in the DefaultClientRequestDirector. This also fixed the problem with the default proxy set at the client level having no effect. Contributed by Oleg Kalnichevski * [HTTPCLIENT-734] Request abort will unblock the thread waiting for a connection Contributed by Sam Berlin * [HTTPCLIENT-759] Ensure release of connections back to the connection manager on exceptions. Contributed by Sam Berlin * [HTTPCLIENT-758] Fixed the use of generics in AbstractHttpClient #removeRequestInterceptorByClass and #removeResponseInterceptorByClass Contributed by Johannes Koch * [HTTPCLIENT-749] HttpParams beans Contributed by Stojce Dimski * [HTTPCLIENT-755] Workaround for known bugs in java.net.URI.resolve() Bug ID: 4708535 Contributed by Johannes Koch -------------------------------------- Release 4.0 Alpha 3 ------------------- ALPHA3 release brings another round of API refinements and improvements in functionality. As of this release HttpClient requires Java 5 compatible runtime environment and takes full advantage of generics and new concurrency primitives. This release also introduces new default cookie policy that selects a cookie specification depending on the format of cookies sent by the target host. It is no longer necessary to know beforehand what kind of HTTP cookie support the target host provides. HttpClient is now able to pick up either a lenient or a strict cookie policy depending on the compliance level of the target host. Another notable improvement is a completely reworked support for multipart entities based on Apache mime4j library. ------------------- HttpClient 3.x features that have NOT yet been ported: ------------------- * NTLM authentication scheme ------------------- Changelog: ------------------- * [HTTPCLIENT-742] common interface for HttpRoute and RouteTracker Contributed by Roland Weber * [HTTPCLIENT-741] Fixed concurrency issues in AbstractClientConnAdapter. Contributed by Oleg Kalnichevski * [HTTPCLIENT-726] testcase for spurious wakeups in ThreadSafeClientConnManager Contributed by Roland Weber * [HTTPCLIENT-643] Automatic connect fail-over for multi-home remote servers. Contributed by Oleg Kalnichevski * [HTTPCLIENT-735] unsetting of DEFAULT_PROXY and FORCED_ROUTE in hierarchies Contributed by Roland Weber * [HTTPCLIENT-723] route planner based on java.net.ProxySelector Contributed by Roland Weber * [HTTPCLIENT-740] don't start connection GC thread in pool constructor Contributed by Roland Weber * [HTTPCLIENT-736] route planners use SchemeRegistry instead of ConnManager Contributed by Roland Weber * [HTTPCLIENT-730] Fixed rewriting of URIs containing escaped characters Contributed by Sam Berlin and Oleg Kalnichevski * [HTTPCLIENT-667] Added 'Meta' cookie policy that selects a cookie specification depending on the format of the cookie(s). Contributed by Oleg Kalnichevski * [HTTPCLIENT-729] Move HttpRoute and related classes to routing package. Contributed by Roland Weber * [HTTPCLIENT-725] Use TimeUnit arguments for timeouts in connection manager. Contributed by Roland Weber * [HTTPCLIENT-677] Connection manager no longer uses Thread.interrupt(). Contributed by Roland Weber * [HTTPCLIENT-716] Allow application-defined routes. Contributed by Roland Weber * [HTTPCLIENT-712] Improve HttpRoute API Contributed by Roland Weber * [HTTPCLIENT-711] Bad route computed for redirected requests Contributed by Oleg Kalnichevski * [HTTPCLIENT-715] Remove RoutedRequest from API Contributed by Roland Weber * [HTTPCLIENT-705] Fixed incorrect handling of URIs with null path component. Contributed by Oleg Kalnichevski * [HTTPCLIENT-688] HttpOptions#getAllowedMethods can now handle multiple Allow headers. Contributed by Andrea Selva -------------------------------------- Release 4.0 Alpha 2 ------------------- ALPHA2 release is another milestone in the redesign of HttpClient. It includes a number of improvements since ALPHA1, among which are improved connection pooling, support for proxy chains, redesigned HTTP state and authentication credentials management API, improved RFC 2965 cookie specification. ------------------- HttpClient 3.x features that have NOT yet been ported ------------------- * NTLM authentication scheme * Support for multipart MIME coded entities ------------------- Changelog ------------------- * [HTTPCLIENT-698] Resolve non-absolute redirect URIs relative to the request URI Contributed by Johannes Koch * [HTTPCLIENT-697] Throw a more intelligible exception when connection to a remote host cannot be established. Contributed by Oleg Kalnichevski * [HTTPCLIENT-689] Caching of SimpleDateFormat in DateUtils Contributed by Daniel Müller * [HTTPCLIENT-689] stackable parameters in AbstractHttpClient Contributed by Roland Weber * [HTTPCLIENT-477] Use distinct instances of the authentication handler interface for authentication with target and proxy hosts Contributed by Oleg Kalnichevski * [HTTPCLIENT-690] ManagedClientConnection provides access to SSLSession Contributed by Roland Weber * [HTTPCLIENT-692] ClientConnectionManager throws InterruptedException Contributed by Roland Weber * [HTTPCORE-116] moved parameter names to interfaces Contributed by Roland Weber * [HTTPCLIENT-649] support for proxy chains in HttpConn Contributed by Roland Weber * [HTTPCLIENT-636] refactor ThreadSafeClientConnManager in separate package Contributed by Roland Weber * [HTTPCLIENT-669] new HttpRoutePlanner interface and implementation Contributed by Andrea Selva * [HTTPCLIENT-653] detached connection wrapper no longer prevents garbage collection of ThreadSafeClientConnManager Contributed by Roland Weber * [HTTPCLIENT-674] use org.apache.http.util.VersionInfo instead of a local one Contributed by Roland Weber * [HTTPCLIENT-666] Replaced HttpState with CredentialsProvier and CookieStore interfaces Contributed by Oleg Kalnichevski * [HTTPCORE-100] revised HttpContext hierarchy Contributed by Roland Weber * [HTTPCLIENT-618] eliminate class HostConfiguration Contributed by Roland Weber * [HTTPCLIENT-672] re-sync with API changes in core alpha6-SNAPSHOT Contributed by Roland Weber -------------------------------------- Release 4.0 Alpha 1 ------------------- HttpClient 4.0 represents a complete, ground-up redesign and almost a complete rewrite of the HttpClient 3.x codeline. This release finally addresses several design flaws that existed since the 1.0 release and could not be fixed without a major code overhaul and breaking API compatibility. The HttpClient 4.0 API is still very experimental and is bound to change during the course of the ALPHA development phase. Several important features have not yet been ported to the new API. Architectural changes --------------------- * Redesign of the HttpClient internals addressing all known major architectural shortcomings of the 3.x codeline * Cleaner, more flexible and expressive API * Better performance and smaller memory footprint due to a more efficient HTTP transport based on HttpCore. HttpClient 4.0 is expected to be 10% to 25% faster than HttpClient 3.x codeline * More modular structure * Pluggable redirect and authentication handlers * Support for protocol incerceptors * Improved connection management * Improved support for sending requests via a proxy or a chain of proxies * Improved handling redirects of entity enclosing requests * More flexible SSL context customization * Reduced intermediate garbage in the process of generating HTTP requests and parsing HTTP responses ------------------- HttpClient 3.x features that have NOT yet been ported ------------------- * NTLM authentication scheme * RFC2965 cookie policy (Cookie2) * Support for multipart MIME coded entities ------------------- Changelog ------------------- The following is a list of contributions tracked in JIRA. Note that this is not a complete list of contributions or changes. Since the API was redesigned completely, tracking everything outside of the source code repository would have been too burdensome. * [HTTPCLIENT-655] User-Agent string no longer violates RFC Contributed by Oleg Kalnichevski * [HTTPCLIENT-541] Virtual host API redesign Contributed by Oleg Kalnichevski * [HTTPCLIENT-614] Allow for different strategies when checking CN of x509 certificates Contributed by Julius Davies * [HTTPCLIENT-136] Fixed inadequate proxy support Long standing architectural problem. Issue opened on 19/Dec/2002. Contributed by Oleg Kalnichevski * [HTTPCLIENT-63] Support for pluggable redirect and authentication handlers Long standing architectural problem. Issue opened on 15/Jul/2002. Contributed by Oleg Kalnichevski * [HTTPCLIENT-245] Fixed redirect handling. HttpClient can now automatically handle redirects of entity enclosing requests. Long standing architectural problem. Issue opened on 14/Jul/2003. Contributed by Oleg Kalnichevski * [HTTPCLIENT-613] HTTPS connections now verify CN of x509 certificates Contributed by Julius Davies * [HTTPCLIENT-497] Wire/header logger names consistent with class loggers Contributed by Oleg Kalnichevski * [HTTPCLIENT-484] AuthSSLProtocolSocketFactory in the main distribution Contributed by Oleg Kalnichevski * [HTTPCLIENT-589] Do not consume the remaining response content if the connection is to be closed Contributed by Roland Weber * [HTTPCLIENT-475] Support for unconnected sockets. HTTP requests can now be aborted while network socket is still being connected. Contributed by Roland Weber httpcomponents-client-rel-v5.2.1/SECURITY.md000066400000000000000000000017361434266521000206310ustar00rootroot00000000000000 # Reporting a vulnerability If you believe you found a vulnerability in Apache HttpClient, please contact [the Apache Security Team](https://www.apache.org/security/). httpcomponents-client-rel-v5.2.1/doap_HttpComponents_Client.rdf000066400000000000000000000132641434266521000250220ustar00rootroot00000000000000 2007-11-15 Apache HttpComponents Client Java library implementing an HTTP client based on HttpCore components. HttpClient is a library for client-side HTTP communication built on HttpCore. It provides connection management, cookie management, and authentication. This is the successor to the widely used Jakarta Commons HttpClient 3.1. Java Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing IETF RFC 7230 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content IETF RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Authentication IETF RFC 7235 Hypertext Transfer Protocol Version 2 (HTTP/2) IETF RFC 7540 HPACK: Header Compression for HTTP/2 IETF RFC 7541 Hypertext Transfer Protocol -- HTTP/1.0 IETF RFC 1945 Upgrading to TLS Within HTTP/1.1 IETF RFC 2817 HTTP Over TLS IETF RFC 2818 HTTP State Management Mechanism IETF RFC 6265 httpcomponents-client-rel-v5.2.1/httpclient5-cache/000077500000000000000000000000001434266521000223355ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/pom.xml000066400000000000000000000104461434266521000236570ustar00rootroot00000000000000 4.0.0 org.apache.httpcomponents.client5 httpclient5-parent 5.2.1 httpclient5-cache Apache HttpClient Cache 2010 Apache HttpComponents HttpClient Cache jar org.apache.httpcomponents.client5.httpclient5.cache org.apache.httpcomponents.client5 httpclient5 org.slf4j slf4j-api org.ehcache.modules ehcache-api true org.apache.logging.log4j log4j-slf4j-impl true org.apache.logging.log4j log4j-core test net.spy spymemcached true org.hamcrest hamcrest test org.mockito mockito-core test org.apache.httpcomponents.client5 httpclient5 ${project.version} test-jar test org.junit.jupiter junit-jupiter test org.apache.maven.plugins maven-jar-plugin test-jar maven-project-info-reports-plugin false index dependencies dependency-info summary httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/000077500000000000000000000000001434266521000231245ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/000077500000000000000000000000001434266521000240505ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/000077500000000000000000000000001434266521000247715ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/000077500000000000000000000000001434266521000255605ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/000077500000000000000000000000001434266521000270015ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/000077500000000000000000000000001434266521000273735ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/000077500000000000000000000000001434266521000307365ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000317155ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/000077500000000000000000000000001434266521000327605ustar00rootroot00000000000000CacheResponseStatus.java000066400000000000000000000041171434266521000374750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; /** * This enumeration represents the various ways a response can be generated * by the caching {@link org.apache.hc.client5.http.classic.HttpClient}; * if a request is executed with an {@link HttpCacheContext} * then a parameter with one of these values will be registered in the * context under the key {@link HttpCacheContext#CACHE_RESPONSE_STATUS}. */ public enum CacheResponseStatus { /** The response was generated directly by the caching module. */ CACHE_MODULE_RESPONSE, /** A response was generated from the cache with no requests sent * upstream. */ CACHE_HIT, /** The response came from an upstream server. */ CACHE_MISS, /** The response was generated from the cache after validating the * entry with the origin server. */ VALIDATED, /** The response came from an upstream server after a cache failure */ FAILURE } HeaderConstants.java000066400000000000000000000070761434266521000366430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; /** * Records static constants for various HTTP header names. * * @since 4.1 */ public class HeaderConstants { public static final String GET_METHOD = "GET"; public static final String HEAD_METHOD = "HEAD"; public static final String OPTIONS_METHOD = "OPTIONS"; public static final String PUT_METHOD = "PUT"; public static final String DELETE_METHOD = "DELETE"; public static final String TRACE_METHOD = "TRACE"; public static final String LAST_MODIFIED = "Last-Modified"; public static final String IF_MATCH = "If-Match"; public static final String IF_RANGE = "If-Range"; public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; public static final String IF_NONE_MATCH = "If-None-Match"; public static final String PRAGMA = "Pragma"; public static final String MAX_FORWARDS = "Max-Forwards"; public static final String ETAG = "ETag"; public static final String EXPIRES = "Expires"; public static final String AGE = "Age"; public static final String VARY = "Vary"; public static final String ALLOW = "Allow"; public static final String VIA = "Via"; public static final String PUBLIC = "public"; public static final String PRIVATE = "private"; public static final String CACHE_CONTROL = "Cache-Control"; public static final String CACHE_CONTROL_NO_STORE = "no-store"; public static final String CACHE_CONTROL_NO_CACHE = "no-cache"; public static final String CACHE_CONTROL_MAX_AGE = "max-age"; public static final String CACHE_CONTROL_MAX_STALE = "max-stale"; public static final String CACHE_CONTROL_MIN_FRESH = "min-fresh"; public static final String CACHE_CONTROL_MUST_REVALIDATE = "must-revalidate"; public static final String CACHE_CONTROL_PROXY_REVALIDATE = "proxy-revalidate"; public static final String STALE_IF_ERROR = "stale-if-error"; public static final String STALE_WHILE_REVALIDATE = "stale-while-revalidate"; public static final String WARNING = "Warning"; public static final String RANGE = "Range"; public static final String CONTENT_RANGE = "Content-Range"; public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; public static final String AUTHORIZATION = "Authorization"; } HttpAsyncCacheInvalidator.java000066400000000000000000000057421434266521000406120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.net.URI; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; /** * Given a particular HTTP request / response pair, flush any cache entries * that this exchange would invalidate. * * @since 5.0 */ @Internal public interface HttpAsyncCacheInvalidator { /** * Flush {@link HttpCacheEntry}s invalidated by the given request. * * @param host backend host * @param request request message * @param cacheKeyResolver cache key resolver used by cache storage * @param cacheStorage internal cache storage * @param callback result callback */ Cancellable flushCacheEntriesInvalidatedByRequest( HttpHost host, HttpRequest request, Resolver cacheKeyResolver, HttpAsyncCacheStorage cacheStorage, FutureCallback callback); /** * Flush {@link HttpCacheEntry}s invalidated by the given message exchange. * * @param host backend host * @param request request message * @param response response message * @param cacheKeyResolver cache key resolver used by cache storage * @param cacheStorage internal cache storage * @param callback result callback */ Cancellable flushCacheEntriesInvalidatedByExchange( HttpHost host, HttpRequest request, HttpResponse response, Resolver cacheKeyResolver, HttpAsyncCacheStorage cacheStorage, FutureCallback callback); } HttpAsyncCacheStorage.java000066400000000000000000000067751434266521000377510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.util.Collection; import java.util.Map; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; /** * {@literal HttpAsyncCacheStorage} represents an abstract HTTP cache * storage backend that can then be plugged into the asynchronous * (non-blocking ) request execution * pipeline. *

* Implementations of this interface are expected to be threading-safe. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface HttpAsyncCacheStorage { /** * Store a given cache entry under the given key. * @param key where in the cache to store the entry * @param entry cached response to store * @param callback result callback */ Cancellable putEntry( String key, HttpCacheEntry entry, FutureCallback callback); /** * Retrieves the cache entry stored under the given key * or null if no entry exists under that key. * @param key cache key * @param callback result callback * @return an {@link HttpCacheEntry} or {@code null} if no * entry exists */ Cancellable getEntry( String key, FutureCallback callback); /** * Deletes/invalidates/removes any cache entries currently * stored under the given key. * @param key * @param callback result callback */ Cancellable removeEntry( String key, FutureCallback callback); /** * Atomically applies the given callback to processChallenge an existing cache * entry under a given key. * @param key indicates which entry to modify * @param casOperation the CAS operation to perform. * @param callback result callback */ Cancellable updateEntry( String key, HttpCacheCASOperation casOperation, FutureCallback callback); /** * Retrieves multiple cache entries stored under the given keys. Some implementations * may use a single bulk operation to do the retrieval. * * @param keys cache keys * @param callback result callback */ Cancellable getEntries(Collection keys, FutureCallback> callback); } HttpAsyncCacheStorageAdaptor.java000066400000000000000000000105331434266521000412470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.util.Collection; import java.util.Map; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.util.Args; /** * {@link HttpAsyncCacheStorage} implementation that emulates asynchronous * behavior using an instance of classic {@link HttpCacheStorage}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public final class HttpAsyncCacheStorageAdaptor implements HttpAsyncCacheStorage { private final HttpCacheStorage cacheStorage; public HttpAsyncCacheStorageAdaptor(final HttpCacheStorage cacheStorage) { this.cacheStorage = Args.notNull(cacheStorage, "Cache storage"); } @Override public Cancellable putEntry(final String key, final HttpCacheEntry entry, final FutureCallback callback) { Args.notEmpty(key, "Key"); Args.notNull(entry, "Cache entry"); Args.notNull(callback, "Callback"); try { cacheStorage.putEntry(key, entry); callback.completed(Boolean.TRUE); } catch (final Exception ex) { callback.failed(ex); } return Operations.nonCancellable(); } @Override public Cancellable getEntry(final String key, final FutureCallback callback) { Args.notEmpty(key, "Key"); Args.notNull(callback, "Callback"); try { final HttpCacheEntry entry = cacheStorage.getEntry(key); callback.completed(entry); } catch (final Exception ex) { callback.failed(ex); } return Operations.nonCancellable(); } @Override public Cancellable removeEntry(final String key, final FutureCallback callback) { Args.notEmpty(key, "Key"); Args.notNull(callback, "Callback"); try { cacheStorage.removeEntry(key); callback.completed(Boolean.TRUE); } catch (final Exception ex) { callback.failed(ex); } return Operations.nonCancellable(); } @Override public Cancellable updateEntry( final String key, final HttpCacheCASOperation casOperation, final FutureCallback callback) { Args.notEmpty(key, "Key"); Args.notNull(casOperation, "CAS operation"); Args.notNull(callback, "Callback"); try { cacheStorage.updateEntry(key, casOperation); callback.completed(Boolean.TRUE); } catch (final Exception ex) { callback.failed(ex); } return Operations.nonCancellable(); } @Override public Cancellable getEntries(final Collection keys, final FutureCallback> callback) { Args.notNull(keys, "Key"); Args.notNull(callback, "Callback"); try { callback.completed(cacheStorage.getEntries(keys)); } catch (final Exception ex) { callback.failed(ex); } return Operations.nonCancellable(); } } HttpCacheCASOperation.java000066400000000000000000000036421434266521000376240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Atomic Compare-And-Swap (CAS) cache operation. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpCacheCASOperation { /** * Returns the new cache entry that should replace an existing one. * * @param existing * the cache entry currently in-place in the cache, possibly * {@code null} if nonexistent * @return the cache entry that should replace it, again, * possibly {@code null} if the entry should be deleted */ HttpCacheEntry execute(HttpCacheEntry existing) throws ResourceIOException; } HttpCacheContext.java000066400000000000000000000050271434266521000367600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; /** * Adaptor class that provides convenience type safe setters and getters * for caching {@link HttpContext} attributes. * * @since 4.3 */ public class HttpCacheContext extends HttpClientContext { /** * This is the name under which the {@link CacheResponseStatus} of a request * (for example, whether it resulted in a cache hit) will be recorded if an * {@link HttpContext} is provided during execution. */ public static final String CACHE_RESPONSE_STATUS = "http.cache.response.status"; public static HttpCacheContext adapt(final HttpContext context) { if (context instanceof HttpCacheContext) { return (HttpCacheContext) context; } else { return new HttpCacheContext(context); } } public static HttpCacheContext create() { return new HttpCacheContext(new BasicHttpContext()); } public HttpCacheContext(final HttpContext context) { super(context); } public HttpCacheContext() { super(); } public CacheResponseStatus getCacheResponseStatus() { return getAttribute(CACHE_RESPONSE_STATUS, CacheResponseStatus.class); } } HttpCacheEntry.java000066400000000000000000000330421434266521000364330ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.io.Serializable; import java.time.Instant; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.HeaderGroup; import org.apache.hc.core5.util.Args; /** * Structure used to store an {@link org.apache.hc.core5.http.HttpResponse} in a cache. * Some entries can optionally depend on system resources that may require * explicit deallocation. In such a case {@link #getResource()} should return * a non null instance of {@link Resource} that must be deallocated by calling * {@link Resource#dispose()} method when no longer used. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class HttpCacheEntry implements MessageHeaders, Serializable { private static final long serialVersionUID = -6300496422359477413L; private static final String REQUEST_METHOD_HEADER_NAME = "Hc-Request-Method"; private final Instant requestDate; private final Instant responseDate; private final int status; private final HeaderGroup responseHeaders; private final Resource resource; private final Map variantMap; private final Instant date; /** * Create a new {@link HttpCacheEntry} with variants. * @param requestDate * Date/time when the request was made (Used for age * calculations) * @param responseDate * Date/time that the response came back (Used for age * calculations) * @param status * HTTP status from origin response * @param responseHeaders * Header[] from original HTTP Response * @param resource representing origin response body * @param variantMap describing cache entries that are variants * of this parent entry; this maps a "variant key" (derived * from the varying request headers) to a "cache key" (where * in the cache storage the particular variant is located) * @deprecated Use {{@link #HttpCacheEntry(Instant, Instant, int, Header[], Resource, Map)}} */ @Deprecated public HttpCacheEntry( final Date requestDate, final Date responseDate, final int status, final Header[] responseHeaders, final Resource resource, final Map variantMap) { super(); Args.notNull(requestDate, "Request date"); Args.notNull(responseDate, "Response date"); Args.check(status >= HttpStatus.SC_SUCCESS, "Status code"); Args.notNull(responseHeaders, "Response headers"); this.requestDate = DateUtils.toInstant(requestDate); this.responseDate = DateUtils.toInstant(responseDate); this.status = status; this.responseHeaders = new HeaderGroup(); this.responseHeaders.setHeaders(responseHeaders); this.resource = resource; this.variantMap = variantMap != null ? new HashMap<>(variantMap) : null; this.date = parseDate(); } /** * Create a new {@link HttpCacheEntry} with variants. * * @param requestDate Date/time when the request was made (Used for age calculations) * @param responseDate Date/time that the response came back (Used for age calculations) * @param status HTTP status from origin response * @param responseHeaders Header[] from original HTTP Response * @param resource representing origin response body * @param variantMap describing cache entries that are variants of this parent entry; this * maps a "variant key" (derived from the varying request headers) to a * "cache key" (where in the cache storage the particular variant is * located) * @since 5.2 */ public HttpCacheEntry( final Instant requestDate, final Instant responseDate, final int status, final Header[] responseHeaders, final Resource resource, final Map variantMap) { super(); Args.notNull(requestDate, "Request date"); Args.notNull(responseDate, "Response date"); Args.check(status >= HttpStatus.SC_SUCCESS, "Status code"); Args.notNull(responseHeaders, "Response headers"); this.requestDate = requestDate; this.responseDate = responseDate; this.status = status; this.responseHeaders = new HeaderGroup(); this.responseHeaders.setHeaders(responseHeaders); this.resource = resource; this.variantMap = variantMap != null ? new HashMap<>(variantMap) : null; this.date = parseDate(); } /** * Create a new {@link HttpCacheEntry}. * * @param requestDate Date/time when the request was made (Used for age calculations) * @param responseDate Date/time that the response came back (Used for age calculations) * @param status HTTP status from origin response * @param responseHeaders Header[] from original HTTP Response * @param resource representing origin response body * @deprecated {{@link #HttpCacheEntry(Instant, Instant, int, Header[], Resource)}} */ @Deprecated public HttpCacheEntry(final Date requestDate, final Date responseDate, final int status, final Header[] responseHeaders, final Resource resource) { this(requestDate, responseDate, status, responseHeaders, resource, new HashMap<>()); } /** * Create a new {@link HttpCacheEntry}. * * @param requestDate * Date/time when the request was made (Used for age * calculations) * @param responseDate * Date/time that the response came back (Used for age * calculations) * @param status * HTTP status from origin response * @param responseHeaders * Header[] from original HTTP Response * @param resource representing origin response body */ public HttpCacheEntry(final Instant requestDate, final Instant responseDate, final int status, final Header[] responseHeaders, final Resource resource) { this(requestDate, responseDate, status, responseHeaders, resource, new HashMap<>()); } /** * Find the "Date" response header and parse it into a {@link Instant} * @return the Date value of the header or null if the header is not present */ private Instant parseDate() { return DateUtils.parseStandardDate(this, HttpHeaders.DATE); } /** * Returns the status from the origin {@link org.apache.hc.core5.http.HttpResponse}. */ public int getStatus() { return this.status; } /** * Returns the time the associated origin request was initiated by the * caching module. * @return {@link Date} * @deprecated USe {@link #getRequestInstant()} */ @Deprecated public Date getRequestDate() { return DateUtils.toDate(requestDate); } /** * Returns the time the associated origin request was initiated by the * caching module. * @return {@link Instant} * @since 5.2 */ public Instant getRequestInstant() { return requestDate; } /** * Returns the time the origin response was received by the caching module. * @return {@link Date} * @deprecated Use {@link #getResponseInstant()} */ @Deprecated public Date getResponseDate() { return DateUtils.toDate(responseDate); } /** * Returns the time the origin response was received by the caching module. * * @return {@link Instant} * @since 5.2 */ public Instant getResponseInstant() { return responseDate; } /** * Returns all the headers that were on the origin response. */ @Override public Header[] getHeaders() { final HeaderGroup filteredHeaders = new HeaderGroup(); for (final Iterator
iterator = responseHeaders.headerIterator(); iterator.hasNext();) { final Header header = iterator.next(); if (!REQUEST_METHOD_HEADER_NAME.equals(header.getName())) { filteredHeaders.addHeader(header); } } return filteredHeaders.getHeaders(); } /** * Returns the first header from the origin response with the given * name. */ @Override public Header getFirstHeader(final String name) { if (REQUEST_METHOD_HEADER_NAME.equalsIgnoreCase(name)) { return null; } return responseHeaders.getFirstHeader(name); } /** * @since 5.0 */ @Override public Header getLastHeader(final String name) { return responseHeaders.getLastHeader(name); } /** * Gets all the headers with the given name that were on the origin * response. */ @Override public Header[] getHeaders(final String name) { if (REQUEST_METHOD_HEADER_NAME.equalsIgnoreCase(name)) { return new Header[0]; } return responseHeaders.getHeaders(name); } /** * @since 5.0 */ @Override public boolean containsHeader(final String name) { return responseHeaders.containsHeader(name); } /** * @since 5.0 */ @Override public int countHeaders(final String name) { return responseHeaders.countHeaders(name); } /** * @since 5.0 */ @Override public Header getHeader(final String name) throws ProtocolException { return responseHeaders.getHeader(name); } /** * @since 5.0 */ @Override public Iterator
headerIterator() { return responseHeaders.headerIterator(); } /** * @since 5.0 */ @Override public Iterator
headerIterator(final String name) { return responseHeaders.headerIterator(name); } /** * Gets the Date value of the "Date" header or null if the header is missing or cannot be * parsed. * * @since 4.3 */ public Date getDate() { return DateUtils.toDate(date); } public Instant getInstant() { return date; } /** * Returns the {@link Resource} containing the origin response body. */ public Resource getResource() { return this.resource; } /** * Indicates whether the origin response indicated the associated * resource had variants (i.e. that the Vary header was set on the * origin response). * @return {@code true} if this cached response was a variant */ public boolean hasVariants() { return getFirstHeader(HeaderConstants.VARY) != null; } /** * Returns an index about where in the cache different variants for * a given resource are stored. This maps "variant keys" to "cache keys", * where the variant key is derived from the varying request headers, * and the cache key is the location in the * {@link HttpCacheStorage} where that * particular variant is stored. The first variant returned is used as * the "parent" entry to hold this index of the other variants. */ public Map getVariantMap() { return Collections.unmodifiableMap(variantMap); } /** * Returns the HTTP request method that was used to create the cached * response entry. * * @since 4.4 */ public String getRequestMethod() { final Header requestMethodHeader = responseHeaders.getFirstHeader(REQUEST_METHOD_HEADER_NAME); if (requestMethodHeader != null) { return requestMethodHeader.getValue(); } return HeaderConstants.GET_METHOD; } /** * Provides a string representation of this instance suitable for * human consumption. */ @Override public String toString() { return "[request date=" + this.requestDate + "; response date=" + this.responseDate + "; status=" + this.status + "]"; } } HttpCacheEntrySerializer.java000066400000000000000000000040651434266521000404700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Serializer / deserializer for {@link HttpCacheStorageEntry} entries. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpCacheEntrySerializer { /** * Serializes the given entry. * * @param entry cache entry * @return serialized representation of the cache entry * @throws ResourceIOException */ T serialize(HttpCacheStorageEntry entry) throws ResourceIOException; /** * Deserializes a cache entry from its serialized representation. * @param serializedObject serialized representation of the cache entry * @return cache entry * @throws ResourceIOException */ HttpCacheStorageEntry deserialize(T serializedObject) throws ResourceIOException; } HttpCacheInvalidator.java000066400000000000000000000056001434266521000376050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.net.URI; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; /** * Given a particular HTTP request / response pair, flush any cache entries * that this exchange would invalidate. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public interface HttpCacheInvalidator { /** * Flush {@link HttpCacheEntry}s invalidated by the given request. * * @param host backend host * @param request request message * @param cacheKeyResolver cache key resolver used by cache storage * @param cacheStorage internal cache storage * * @since 5.0 */ void flushCacheEntriesInvalidatedByRequest( HttpHost host, HttpRequest request, Resolver cacheKeyResolver, HttpCacheStorage cacheStorage); /** * Flush {@link HttpCacheEntry}s invalidated by the given message exchange. * * @param host backend host * @param request request message * @param response response message * @param cacheKeyResolver cache key resolver used by cache storage * @param cacheStorage internal cache storage * * @since 5.0 */ void flushCacheEntriesInvalidatedByExchange( HttpHost host, HttpRequest request, HttpResponse response, Resolver cacheKeyResolver, HttpCacheStorage cacheStorage); } HttpCacheStorage.java000066400000000000000000000065531434266521000367450ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.util.Collection; import java.util.Map; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * {@literal HttpCacheStorage} represents an abstract HTTP cache * storage backend that can then be plugged into the classic * (blocking) request execution pipeline. *

* Implementations of this interface are expected to be threading-safe. *

* * @since 4.1 */ @Contract(threading = ThreadingBehavior.SAFE) public interface HttpCacheStorage { /** * Store a given cache entry under the given key. * @param key where in the cache to store the entry * @param entry cached response to store * @throws ResourceIOException */ void putEntry(String key, HttpCacheEntry entry) throws ResourceIOException; /** * Retrieves the cache entry stored under the given key * or null if no entry exists under that key. * @param key cache key * @return an {@link HttpCacheEntry} or {@code null} if no * entry exists * @throws ResourceIOException */ HttpCacheEntry getEntry(String key) throws ResourceIOException; /** * Deletes/invalidates/removes any cache entries currently * stored under the given key. * @param key * @throws ResourceIOException */ void removeEntry(String key) throws ResourceIOException; /** * Atomically applies the given callback to processChallenge an existing cache * entry under a given key. * @param key indicates which entry to modify * @param casOperation the CAS operation to perform. * @throws ResourceIOException * @throws HttpCacheUpdateException */ void updateEntry( String key, HttpCacheCASOperation casOperation) throws ResourceIOException, HttpCacheUpdateException; /** * Retrieves multiple cache entries stored under the given keys. Some implementations * may use a single bulk operation to do the retrieval. * * @param keys cache keys * @return an map of {@link HttpCacheEntry}s. * * @since 5.0 */ Map getEntries(Collection keys) throws ResourceIOException; } HttpCacheStorageEntry.java000066400000000000000000000041551434266521000377630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * This class contains a {@link HttpCacheEntry} along with its key. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class HttpCacheStorageEntry implements Serializable { private static final long serialVersionUID = 1L; private final String key; private final HttpCacheEntry content; public HttpCacheStorageEntry(final String key, final HttpCacheEntry content) { this.key = key; this.content = Args.notNull(content, "Cache entry"); } public String getKey() { return key; } public HttpCacheEntry getContent() { return content; } @Override public String toString() { return "[key=" + key + "; content=" + content + "]"; } } HttpCacheUpdateException.java000066400000000000000000000032601434266521000404320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; /** * Signals that {@link HttpCacheStorage} encountered an error performing * an update operation. * * @since 4.1 */ public class HttpCacheUpdateException extends Exception { private static final long serialVersionUID = 823573584868632876L; public HttpCacheUpdateException(final String message) { super(message); } public HttpCacheUpdateException(final String message, final Throwable cause) { super(message); initCause(cause); } } Resource.java000066400000000000000000000052531434266521000353400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.Serializable; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Represents a disposable system resource used for handling * cached response bodies. *

* Implementations of this interface are expected to be threading-safe. *

* * @since 4.1 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class Resource implements Serializable { private static final long serialVersionUID = 1L; /** * Returns resource content as a {@link InputStream}. * * @throws ResourceIOException */ public InputStream getInputStream() throws ResourceIOException { return new ByteArrayInputStream(get()); } /** * Returns resource content as a byte array. *

* Please note for memory efficiency some resource implementations * may return a reference to the underlying byte array. The returned * value should be treated as immutable. * * @throws ResourceIOException * * @since 5.0 */ public abstract byte[] get() throws ResourceIOException; /** * Returns the length in bytes of the response body. */ public abstract long length(); /** * Indicates the system no longer needs to keep this * response body and any system resources associated with * it may be reclaimed. */ public abstract void dispose(); } ResourceFactory.java000066400000000000000000000056511434266521000366720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Generates {@link Resource} instances for handling cached * HTTP response bodies. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ResourceFactory { /** * Creates a {@link Resource} from a given response body. * @param requestId a unique identifier for this particular response body. * @param content byte array that represents the origin HTTP response body. * @return a {@code Resource} containing however much of * the response body was successfully read. * @throws ResourceIOException */ Resource generate(String requestId, byte[] content) throws ResourceIOException; /** * Creates a {@link Resource} from a given response body. * @param requestId a unique identifier for this particular response body. * @param content byte array that represents the origin HTTP response body. * @param off the start offset in the array. * @param len the number of bytes to read from the array. * @return a {@code Resource} containing however much of * the response body was successfully read. * @throws ResourceIOException */ Resource generate(String requestId, byte[] content, int off, int len) throws ResourceIOException; /** * Clones an existing {@link Resource}. * @param requestId unique identifier provided to associate * with the cloned response body. * @param resource the original response body to clone. * @return the {@code Resource} copy * @throws ResourceIOException */ Resource copy(String requestId, Resource resource) throws ResourceIOException; } ResourceIOException.java000066400000000000000000000031461434266521000374460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import java.io.IOException; /** * Signals a generic resource I/O error. */ public class ResourceIOException extends IOException { private static final long serialVersionUID = 1L; public ResourceIOException(final String message) { super(message); } public ResourceIOException(final String message, final Throwable cause) { super(message); initCause(cause); } } package-info.java000066400000000000000000000024411434266521000360710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Caching APIs for both the classic and the asynchronous * HTTP transports. */ package org.apache.hc.client5.http.cache; httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/000077500000000000000000000000001434266521000326565ustar00rootroot00000000000000cache/000077500000000000000000000000001434266521000336425ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/implAbstractBinaryAsyncCacheStorage.java000066400000000000000000000035221434266521000426660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; /** * Abstract cache backend for serialized binary objects capable of CAS (compare-and-swap) updates. * * @since 5.0 */ public abstract class AbstractBinaryAsyncCacheStorage extends AbstractSerializingAsyncCacheStorage { public AbstractBinaryAsyncCacheStorage(final int maxUpdateRetries, final HttpCacheEntrySerializer serializer) { super(maxUpdateRetries, serializer); } public AbstractBinaryAsyncCacheStorage(final int maxUpdateRetries) { super(maxUpdateRetries, ByteArrayCacheEntrySerializer.INSTANCE); } } AbstractBinaryCacheStorage.java000066400000000000000000000034761434266521000417000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; /** * Abstract cache backend for serialized binary objects capable of CAS (compare-and-swap) updates. * * @since 5.0 */ public abstract class AbstractBinaryCacheStorage extends AbstractSerializingCacheStorage { public AbstractBinaryCacheStorage(final int maxUpdateRetries, final HttpCacheEntrySerializer serializer) { super(maxUpdateRetries, serializer); } public AbstractBinaryCacheStorage(final int maxUpdateRetries) { super(maxUpdateRetries, ByteArrayCacheEntrySerializer.INSTANCE); } } AbstractSerializingAsyncCacheStorage.java000066400000000000000000000276221434266521000437310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheCASOperation; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.HttpCacheUpdateException; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.ComplexCancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.util.Args; /** * Abstract cache backend for serialized objects capable of CAS (compare-and-swap) updates. * * @since 5.0 */ public abstract class AbstractSerializingAsyncCacheStorage implements HttpAsyncCacheStorage { private final int maxUpdateRetries; private final HttpCacheEntrySerializer serializer; public AbstractSerializingAsyncCacheStorage(final int maxUpdateRetries, final HttpCacheEntrySerializer serializer) { this.maxUpdateRetries = Args.notNegative(maxUpdateRetries, "Max retries"); this.serializer = Args.notNull(serializer, "Cache entry serializer"); } protected abstract String digestToStorageKey(String key); protected abstract T getStorageObject(CAS cas) throws ResourceIOException; protected abstract Cancellable store(String storageKey, T storageObject, FutureCallback callback); protected abstract Cancellable restore(String storageKey, FutureCallback callback); protected abstract Cancellable getForUpdateCAS(String storageKey, FutureCallback callback); protected abstract Cancellable updateCAS(String storageKey, CAS cas, T storageObject, FutureCallback callback); protected abstract Cancellable delete(String storageKey, FutureCallback callback); protected abstract Cancellable bulkRestore(Collection storageKeys, FutureCallback> callback); @Override public final Cancellable putEntry( final String key, final HttpCacheEntry entry, final FutureCallback callback) { Args.notNull(key, "Storage key"); Args.notNull(callback, "Callback"); try { final String storageKey = digestToStorageKey(key); final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, entry)); return store(storageKey, storageObject, callback); } catch (final Exception ex) { callback.failed(ex); return Operations.nonCancellable(); } } @Override public final Cancellable getEntry(final String key, final FutureCallback callback) { Args.notNull(key, "Storage key"); Args.notNull(callback, "Callback"); try { final String storageKey = digestToStorageKey(key); return restore(storageKey, new FutureCallback() { @Override public void completed(final T storageObject) { try { if (storageObject != null) { final HttpCacheStorageEntry entry = serializer.deserialize(storageObject); if (key.equals(entry.getKey())) { callback.completed(entry.getContent()); } else { callback.completed(null); } } else { callback.completed(null); } } catch (final Exception ex) { callback.failed(ex); } } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } catch (final Exception ex) { callback.failed(ex); return Operations.nonCancellable(); } } @Override public final Cancellable removeEntry(final String key, final FutureCallback callback) { Args.notNull(key, "Storage key"); Args.notNull(callback, "Callback"); try { final String storageKey = digestToStorageKey(key); return delete(storageKey, callback); } catch (final Exception ex) { callback.failed(ex); return Operations.nonCancellable(); } } @Override public final Cancellable updateEntry( final String key, final HttpCacheCASOperation casOperation, final FutureCallback callback) { Args.notNull(key, "Storage key"); Args.notNull(casOperation, "CAS operation"); Args.notNull(callback, "Callback"); final ComplexCancellable complexCancellable = new ComplexCancellable(); final AtomicInteger count = new AtomicInteger(0); attemptUpdateEntry(key, casOperation, complexCancellable, count, callback); return complexCancellable; } private void attemptUpdateEntry( final String key, final HttpCacheCASOperation casOperation, final ComplexCancellable complexCancellable, final AtomicInteger count, final FutureCallback callback) { try { final String storageKey = digestToStorageKey(key); complexCancellable.setDependency(getForUpdateCAS(storageKey, new FutureCallback() { @Override public void completed(final CAS cas) { try { HttpCacheStorageEntry storageEntry = cas != null ? serializer.deserialize(getStorageObject(cas)) : null; if (storageEntry != null && !key.equals(storageEntry.getKey())) { storageEntry = null; } final HttpCacheEntry existingEntry = storageEntry != null ? storageEntry.getContent() : null; final HttpCacheEntry updatedEntry = casOperation.execute(existingEntry); if (existingEntry == null) { putEntry(key, updatedEntry, callback); } else { final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, updatedEntry)); complexCancellable.setDependency(updateCAS(storageKey, cas, storageObject, new FutureCallback() { @Override public void completed(final Boolean result) { if (result.booleanValue()) { callback.completed(result); } else { if (!complexCancellable.isCancelled()) { final int numRetries = count.incrementAndGet(); if (numRetries >= maxUpdateRetries) { callback.failed(new HttpCacheUpdateException("Cache update failed after " + numRetries + " retries")); } else { attemptUpdateEntry(key, casOperation, complexCancellable, count, callback); } } } } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } })); } } catch (final Exception ex) { callback.failed(ex); } } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } })); } catch (final Exception ex) { callback.failed(ex); } } @Override public final Cancellable getEntries(final Collection keys, final FutureCallback> callback) { Args.notNull(keys, "Storage keys"); Args.notNull(callback, "Callback"); try { final List storageKeys = new ArrayList<>(keys.size()); for (final String key: keys) { storageKeys.add(digestToStorageKey(key)); } return bulkRestore(storageKeys, new FutureCallback>() { @Override public void completed(final Map storageObjectMap) { try { final Map resultMap = new HashMap<>(); for (final String key: keys) { final String storageKey = digestToStorageKey(key); final T storageObject = storageObjectMap.get(storageKey); if (storageObject != null) { final HttpCacheStorageEntry entry = serializer.deserialize(storageObject); if (key.equals(entry.getKey())) { resultMap.put(key, entry.getContent()); } } } callback.completed(resultMap); } catch (final Exception ex) { callback.failed(ex); } } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } catch (final Exception ex) { callback.failed(ex); return Operations.nonCancellable(); } } } AbstractSerializingCacheStorage.java000066400000000000000000000145751434266521000427360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheCASOperation; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.HttpCacheUpdateException; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.util.Args; /** * Abstract cache backend for serialized objects capable of CAS (compare-and-swap) updates. * * @since 5.0 */ public abstract class AbstractSerializingCacheStorage implements HttpCacheStorage { private final int maxUpdateRetries; private final HttpCacheEntrySerializer serializer; public AbstractSerializingCacheStorage(final int maxUpdateRetries, final HttpCacheEntrySerializer serializer) { this.maxUpdateRetries = Args.notNegative(maxUpdateRetries, "Max retries"); this.serializer = Args.notNull(serializer, "Cache entry serializer"); } protected abstract String digestToStorageKey(String key); protected abstract void store(String storageKey, T storageObject) throws ResourceIOException; protected abstract T restore(String storageKey) throws ResourceIOException; protected abstract CAS getForUpdateCAS(String storageKey) throws ResourceIOException; protected abstract T getStorageObject(CAS cas) throws ResourceIOException; protected abstract boolean updateCAS(String storageKey, CAS cas, T storageObject) throws ResourceIOException; protected abstract void delete(String storageKey) throws ResourceIOException; protected abstract Map bulkRestore(Collection storageKeys) throws ResourceIOException; @Override public final void putEntry(final String key, final HttpCacheEntry entry) throws ResourceIOException { final String storageKey = digestToStorageKey(key); final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, entry)); store(storageKey, storageObject); } @Override public final HttpCacheEntry getEntry(final String key) throws ResourceIOException { final String storageKey = digestToStorageKey(key); final T storageObject = restore(storageKey); if (storageObject == null) { return null; } final HttpCacheStorageEntry entry = serializer.deserialize(storageObject); if (key.equals(entry.getKey())) { return entry.getContent(); } else { return null; } } @Override public final void removeEntry(final String key) throws ResourceIOException { final String storageKey = digestToStorageKey(key); delete(storageKey); } @Override public final void updateEntry( final String key, final HttpCacheCASOperation casOperation) throws HttpCacheUpdateException, ResourceIOException { int numRetries = 0; final String storageKey = digestToStorageKey(key); for (;;) { final CAS cas = getForUpdateCAS(storageKey); HttpCacheStorageEntry storageEntry = cas != null ? serializer.deserialize(getStorageObject(cas)) : null; if (storageEntry != null && !key.equals(storageEntry.getKey())) { storageEntry = null; } final HttpCacheEntry existingEntry = storageEntry != null ? storageEntry.getContent() : null; final HttpCacheEntry updatedEntry = casOperation.execute(existingEntry); if (existingEntry == null) { putEntry(key, updatedEntry); return; } final T storageObject = serializer.serialize(new HttpCacheStorageEntry(key, updatedEntry)); if (!updateCAS(storageKey, cas, storageObject)) { numRetries++; if (numRetries >= maxUpdateRetries) { throw new HttpCacheUpdateException("Cache update failed after " + numRetries + " retries"); } } else { return; } } } @Override public final Map getEntries(final Collection keys) throws ResourceIOException { Args.notNull(keys, "Storage keys"); final List storageKeys = new ArrayList<>(keys.size()); for (final String key: keys) { storageKeys.add(digestToStorageKey(key)); } final Map storageObjectMap = bulkRestore(storageKeys); final Map resultMap = new HashMap<>(); for (final String key: keys) { final String storageKey = digestToStorageKey(key); final T storageObject = storageObjectMap.get(storageKey); if (storageObject != null) { final HttpCacheStorageEntry entry = serializer.deserialize(storageObject); if (key.equals(entry.getKey())) { resultMap.put(key, entry.getContent()); } } } return resultMap; } } AsyncCachingExec.java000066400000000000000000001401661434266521000376540ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.time.Instant; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.async.methods.SimpleBody; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.cache.CacheResponseStatus; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request executor in the request execution chain that is responsible for * transparent client-side caching. *

* The current implementation is conditionally * compliant with HTTP/1.1 (meaning all the MUST and MUST NOTs are obeyed), * although quite a lot, though not all, of the SHOULDs and SHOULD NOTs * are obeyed too. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) // So long as the responseCache implementation is threadsafe class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(AsyncCachingExec.class); private final HttpAsyncCache responseCache; private final DefaultAsyncCacheRevalidator cacheRevalidator; private final ConditionalRequestBuilder conditionalRequestBuilder; AsyncCachingExec(final HttpAsyncCache cache, final DefaultAsyncCacheRevalidator cacheRevalidator, final CacheConfig config) { super(config); this.responseCache = Args.notNull(cache, "Response cache"); this.cacheRevalidator = cacheRevalidator; this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(request -> BasicRequestBuilder.copy(request).build()); } AsyncCachingExec( final HttpAsyncCache responseCache, final CacheValidityPolicy validityPolicy, final ResponseCachingPolicy responseCachingPolicy, final CachedHttpResponseGenerator responseGenerator, final CacheableRequestPolicy cacheableRequestPolicy, final CachedResponseSuitabilityChecker suitabilityChecker, final ResponseProtocolCompliance responseCompliance, final RequestProtocolCompliance requestCompliance, final DefaultAsyncCacheRevalidator cacheRevalidator, final ConditionalRequestBuilder conditionalRequestBuilder, final CacheConfig config) { super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy, suitabilityChecker, responseCompliance, requestCompliance, config); this.responseCache = responseCache; this.cacheRevalidator = cacheRevalidator; this.conditionalRequestBuilder = conditionalRequestBuilder; } AsyncCachingExec( final HttpAsyncCache cache, final ScheduledExecutorService executorService, final SchedulingStrategy schedulingStrategy, final CacheConfig config) { this(cache, executorService != null ? new DefaultAsyncCacheRevalidator(executorService, schedulingStrategy) : null, config); } AsyncCachingExec( final ResourceFactory resourceFactory, final HttpAsyncCacheStorage storage, final ScheduledExecutorService executorService, final SchedulingStrategy schedulingStrategy, final CacheConfig config) { this(new BasicHttpAsyncCache(resourceFactory, storage), executorService, schedulingStrategy, config); } private void triggerResponse( final SimpleHttpResponse cacheResponse, final AsyncExecChain.Scope scope, final AsyncExecCallback asyncExecCallback) { scope.clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, cacheResponse); scope.execRuntime.releaseEndpoint(); final SimpleBody body = cacheResponse.getBody(); final byte[] content = body != null ? body.getBodyBytes() : null; final ContentType contentType = body != null ? body.getContentType() : null; try { final AsyncDataConsumer dataConsumer = asyncExecCallback.handleResponse( cacheResponse, content != null ? new BasicEntityDetails(content.length, contentType) : null); if (dataConsumer != null) { if (content != null) { dataConsumer.consume(ByteBuffer.wrap(content)); } dataConsumer.streamEnd(null); } asyncExecCallback.completed(); } catch (final HttpException | IOException ex) { asyncExecCallback.failed(ex); } } static class AsyncExecCallbackWrapper implements AsyncExecCallback { private final AsyncExecCallback asyncExecCallback; private final Runnable command; AsyncExecCallbackWrapper(final AsyncExecCallback asyncExecCallback, final Runnable command) { this.asyncExecCallback = asyncExecCallback; this.command = command; } @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { return null; } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { } @Override public void completed() { command.run(); } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } } @Override public void execute( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final HttpRoute route = scope.route; final CancellableDependency operation = scope.cancellableDependency; final HttpClientContext context = scope.clientContext; context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final URIAuthority authority = request.getAuthority(); final String scheme = request.getScheme(); final HttpHost target = authority != null ? new HttpHost(scheme, authority) : route.getTargetHost(); final String via = generateViaHeader(request); // default response context setResponseStatus(context, CacheResponseStatus.CACHE_MISS); if (clientRequestsOurOptions(request)) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); triggerResponse(SimpleHttpResponse.create(HttpStatus.SC_NOT_IMPLEMENTED), scope, asyncExecCallback); return; } final SimpleHttpResponse fatalErrorResponse = getFatallyNonCompliantResponse(request, context); if (fatalErrorResponse != null) { triggerResponse(fatalErrorResponse, scope, asyncExecCallback); return; } requestCompliance.makeRequestCompliant(request); request.addHeader("Via",via); if (!cacheableRequestPolicy.isServableFromCache(request)) { LOG.debug("Request is not servable from cache"); operation.setDependency(responseCache.flushCacheEntriesInvalidatedByRequest(target, request, new FutureCallback() { @Override public void completed(final Boolean result) { callBackend(target, request, entityProducer, scope, chain, asyncExecCallback); } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } else { operation.setDependency(responseCache.getCacheEntry(target, request, new FutureCallback() { @Override public void completed(final HttpCacheEntry entry) { if (entry == null) { LOG.debug("Cache miss"); handleCacheMiss(target, request, entityProducer, scope, chain, asyncExecCallback); } else { handleCacheHit(target, request, entityProducer, scope, chain, asyncExecCallback, entry); } } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } } void chainProceed( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) { try { chain.proceed(request, entityProducer, scope, asyncExecCallback); } catch (final HttpException | IOException ex) { asyncExecCallback.failed(ex); } } void callBackend( final HttpHost target, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) { LOG.debug("Calling the backend"); final Instant requestDate = getCurrentDate(); final AtomicReference callbackRef = new AtomicReference<>(); chainProceed(request, entityProducer, scope, chain, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse backendResponse, final EntityDetails entityDetails) throws HttpException, IOException { final Instant responseDate = getCurrentDate(); backendResponse.addHeader("Via", generateViaHeader(backendResponse)); final AsyncExecCallback callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback); callbackRef.set(callback); return callback.handleResponse(backendResponse, entityDetails); } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { final AsyncExecCallback callback = callbackRef.getAndSet(null); if (callback != null) { callback.handleInformationResponse(response); } else { asyncExecCallback.handleInformationResponse(response); } } @Override public void completed() { final AsyncExecCallback callback = callbackRef.getAndSet(null); if (callback != null) { callback.completed(); } else { asyncExecCallback.completed(); } } @Override public void failed(final Exception cause) { final AsyncExecCallback callback = callbackRef.getAndSet(null); if (callback != null) { callback.failed(cause); } else { asyncExecCallback.failed(cause); } } }); } class CachingAsyncDataConsumer implements AsyncDataConsumer { private final AsyncExecCallback fallback; private final HttpResponse backendResponse; private final EntityDetails entityDetails; private final AtomicBoolean writtenThrough; private final AtomicReference bufferRef; private final AtomicReference dataConsumerRef; CachingAsyncDataConsumer( final AsyncExecCallback fallback, final HttpResponse backendResponse, final EntityDetails entityDetails) { this.fallback = fallback; this.backendResponse = backendResponse; this.entityDetails = entityDetails; this.writtenThrough = new AtomicBoolean(false); this.bufferRef = new AtomicReference<>(entityDetails != null ? new ByteArrayBuffer(1024) : null); this.dataConsumerRef = new AtomicReference<>(); } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncDataConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public final void consume(final ByteBuffer src) throws IOException { final ByteArrayBuffer buffer = bufferRef.get(); if (buffer != null) { if (src.hasArray()) { buffer.append(src.array(), src.arrayOffset() + src.position(), src.remaining()); } else { while (src.hasRemaining()) { buffer.append(src.get()); } } if (buffer.length() > cacheConfig.getMaxObjectSize()) { LOG.debug("Backend response content length exceeds maximum"); // Over the max limit. Stop buffering and forward the response // along with all the data buffered so far to the caller. bufferRef.set(null); try { final AsyncDataConsumer dataConsumer = fallback.handleResponse(backendResponse, entityDetails); if (dataConsumer != null) { dataConsumerRef.set(dataConsumer); writtenThrough.set(true); dataConsumer.consume(ByteBuffer.wrap(buffer.array(), 0, buffer.length())); } } catch (final HttpException ex) { fallback.failed(ex); } } } else { final AsyncDataConsumer dataConsumer = dataConsumerRef.get(); if (dataConsumer != null) { dataConsumer.consume(src); } } } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { final AsyncDataConsumer dataConsumer = dataConsumerRef.getAndSet(null); if (dataConsumer != null) { dataConsumer.streamEnd(trailers); } } @Override public void releaseResources() { final AsyncDataConsumer dataConsumer = dataConsumerRef.getAndSet(null); if (dataConsumer != null) { dataConsumer.releaseResources(); } } } class BackendResponseHandler implements AsyncExecCallback { private final HttpHost target; private final HttpRequest request; private final Instant requestDate; private final Instant responseDate; private final AsyncExecChain.Scope scope; private final AsyncExecCallback asyncExecCallback; private final AtomicReference cachingConsumerRef; BackendResponseHandler( final HttpHost target, final HttpRequest request, final Instant requestDate, final Instant responseDate, final AsyncExecChain.Scope scope, final AsyncExecCallback asyncExecCallback) { this.target = target; this.request = request; this.requestDate = requestDate; this.responseDate = responseDate; this.scope = scope; this.asyncExecCallback = asyncExecCallback; this.cachingConsumerRef = new AtomicReference<>(); } @Override public AsyncDataConsumer handleResponse( final HttpResponse backendResponse, final EntityDetails entityDetails) throws HttpException, IOException { responseCompliance.ensureProtocolCompliance(scope.originalRequest, request, backendResponse); responseCache.flushCacheEntriesInvalidatedByExchange(target, request, backendResponse, new FutureCallback() { @Override public void completed(final Boolean result) { } @Override public void failed(final Exception ex) { LOG.warn("Unable to flush invalidated entries from cache", ex); } @Override public void cancelled() { } }); final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse); if (cacheable) { cachingConsumerRef.set(new CachingAsyncDataConsumer(asyncExecCallback, backendResponse, entityDetails)); storeRequestIfModifiedSinceFor304Response(request, backendResponse); } else { LOG.debug("Backend response is not cacheable"); responseCache.flushCacheEntriesFor(target, request, new FutureCallback() { @Override public void completed(final Boolean result) { } @Override public void failed(final Exception ex) { LOG.warn("Unable to flush invalidated entries from cache", ex); } @Override public void cancelled() { } }); } final CachingAsyncDataConsumer cachingDataConsumer = cachingConsumerRef.get(); if (cachingDataConsumer != null) { LOG.debug("Caching backend response"); return cachingDataConsumer; } return asyncExecCallback.handleResponse(backendResponse, entityDetails); } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { asyncExecCallback.handleInformationResponse(response); } void triggerNewCacheEntryResponse(final HttpResponse backendResponse, final Instant responseDate, final ByteArrayBuffer buffer) { final CancellableDependency operation = scope.cancellableDependency; operation.setDependency(responseCache.createCacheEntry( target, request, backendResponse, buffer, requestDate, responseDate, new FutureCallback() { @Override public void completed(final HttpCacheEntry newEntry) { LOG.debug("Backend response successfully cached"); try { final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, newEntry); triggerResponse(cacheResponse, scope, asyncExecCallback); } catch (final ResourceIOException ex) { asyncExecCallback.failed(ex); } } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } @Override public void completed() { final CachingAsyncDataConsumer cachingDataConsumer = cachingConsumerRef.getAndSet(null); if (cachingDataConsumer != null && !cachingDataConsumer.writtenThrough.get()) { final ByteArrayBuffer buffer = cachingDataConsumer.bufferRef.getAndSet(null); final HttpResponse backendResponse = cachingDataConsumer.backendResponse; if (cacheConfig.isFreshnessCheckEnabled()) { final CancellableDependency operation = scope.cancellableDependency; operation.setDependency(responseCache.getCacheEntry(target, request, new FutureCallback() { @Override public void completed(final HttpCacheEntry existingEntry) { if (DateSupport.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) { LOG.debug("Backend already contains fresher cache entry"); try { final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, existingEntry); triggerResponse(cacheResponse, scope, asyncExecCallback); } catch (final ResourceIOException ex) { asyncExecCallback.failed(ex); } } else { triggerNewCacheEntryResponse(backendResponse, responseDate, buffer); } } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } else { triggerNewCacheEntryResponse(backendResponse, responseDate, buffer); } } else { asyncExecCallback.completed(); } } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } } private void handleCacheHit( final HttpHost target, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback, final HttpCacheEntry entry) { final HttpClientContext context = scope.clientContext; recordCacheHit(target, request); final Instant now = getCurrentDate(); if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) { LOG.debug("Cache hit"); try { final SimpleHttpResponse cacheResponse = generateCachedResponse(request, context, entry, now); triggerResponse(cacheResponse, scope, asyncExecCallback); } catch (final ResourceIOException ex) { recordCacheFailure(target, request); if (!mayCallBackend(request)) { final SimpleHttpResponse cacheResponse = generateGatewayTimeout(context); triggerResponse(cacheResponse, scope, asyncExecCallback); } else { setResponseStatus(scope.clientContext, CacheResponseStatus.FAILURE); try { chain.proceed(request, entityProducer, scope, asyncExecCallback); } catch (final HttpException | IOException ex2) { asyncExecCallback.failed(ex2); } } } } else if (!mayCallBackend(request)) { LOG.debug("Cache entry not suitable but only-if-cached requested"); final SimpleHttpResponse cacheResponse = generateGatewayTimeout(context); triggerResponse(cacheResponse, scope, asyncExecCallback); } else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) { LOG.debug("Revalidating cache entry"); if (cacheRevalidator != null && !staleResponseNotAllowed(request, entry, now) && validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) { LOG.debug("Serving stale with asynchronous revalidation"); try { final SimpleHttpResponse cacheResponse = generateCachedResponse(request, context, entry, now); final String exchangeId = ExecSupport.getNextExchangeId(); context.setExchangeId(exchangeId); final AsyncExecChain.Scope fork = new AsyncExecChain.Scope( exchangeId, scope.route, scope.originalRequest, new ComplexFuture<>(null), HttpClientContext.create(), scope.execRuntime.fork(), scope.scheduler, scope.execCount); cacheRevalidator.revalidateCacheEntry( responseCache.generateKey(target, request, entry), asyncExecCallback, asyncExecCallback1 -> revalidateCacheEntry(target, request, entityProducer, fork, chain, asyncExecCallback1, entry)); triggerResponse(cacheResponse, scope, asyncExecCallback); } catch (final ResourceIOException ex) { asyncExecCallback.failed(ex); } } else { revalidateCacheEntry(target, request, entityProducer, scope, chain, asyncExecCallback, entry); } } else { LOG.debug("Cache entry not usable; calling backend"); callBackend(target, request, entityProducer, scope, chain, asyncExecCallback); } } void revalidateCacheEntry( final HttpHost target, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback, final HttpCacheEntry cacheEntry) { final Instant requestDate = getCurrentDate(); final HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequest( BasicRequestBuilder.copy(scope.originalRequest).build(), cacheEntry); chainProceed(conditionalRequest, entityProducer, scope, chain, new AsyncExecCallback() { final AtomicReference callbackRef = new AtomicReference<>(); void triggerUpdatedCacheEntryResponse(final HttpResponse backendResponse, final Instant responseDate) { final CancellableDependency operation = scope.cancellableDependency; recordCacheUpdate(scope.clientContext); operation.setDependency(responseCache.updateCacheEntry( target, request, cacheEntry, backendResponse, requestDate, responseDate, new FutureCallback() { @Override public void completed(final HttpCacheEntry updatedEntry) { if (suitabilityChecker.isConditional(request) && suitabilityChecker.allConditionalsMatch(request, updatedEntry, Instant.now())) { final SimpleHttpResponse cacheResponse = responseGenerator.generateNotModifiedResponse(updatedEntry); triggerResponse(cacheResponse, scope, asyncExecCallback); } else { try { final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, updatedEntry); triggerResponse(cacheResponse, scope, asyncExecCallback); } catch (final ResourceIOException ex) { asyncExecCallback.failed(ex); } } } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } void triggerResponseStaleCacheEntry() { try { final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, cacheEntry); cacheResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\""); triggerResponse(cacheResponse, scope, asyncExecCallback); } catch (final ResourceIOException ex) { asyncExecCallback.failed(ex); } } AsyncExecCallback evaluateResponse(final HttpResponse backendResponse, final Instant responseDate) { backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse)); final int statusCode = backendResponse.getCode(); if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) { recordCacheUpdate(scope.clientContext); } if (statusCode == HttpStatus.SC_NOT_MODIFIED) { return new AsyncExecCallbackWrapper(asyncExecCallback, () -> triggerUpdatedCacheEntryResponse(backendResponse, responseDate)); } if (staleIfErrorAppliesTo(statusCode) && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate()) && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) { return new AsyncExecCallbackWrapper(asyncExecCallback, this::triggerResponseStaleCacheEntry); } return new BackendResponseHandler(target, conditionalRequest, requestDate, responseDate, scope, asyncExecCallback); } @Override public AsyncDataConsumer handleResponse( final HttpResponse backendResponse1, final EntityDetails entityDetails) throws HttpException, IOException { final Instant responseDate1 = getCurrentDate(); final AsyncExecCallback callback1; if (revalidationResponseIsTooOld(backendResponse1, cacheEntry) && (entityProducer == null || entityProducer.isRepeatable())) { final HttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest( BasicRequestBuilder.copy(scope.originalRequest).build()); callback1 = new AsyncExecCallbackWrapper(asyncExecCallback, () -> chainProceed(unconditional, entityProducer, scope, chain, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse backendResponse2, final EntityDetails entityDetails1) throws HttpException, IOException { final Instant responseDate2 = getCurrentDate(); final AsyncExecCallback callback2 = evaluateResponse(backendResponse2, responseDate2); callbackRef.set(callback2); return callback2.handleResponse(backendResponse2, entityDetails1); } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { final AsyncExecCallback callback2 = callbackRef.getAndSet(null); if (callback2 != null) { callback2.handleInformationResponse(response); } else { asyncExecCallback.handleInformationResponse(response); } } @Override public void completed() { final AsyncExecCallback callback2 = callbackRef.getAndSet(null); if (callback2 != null) { callback2.completed(); } else { asyncExecCallback.completed(); } } @Override public void failed(final Exception cause) { final AsyncExecCallback callback2 = callbackRef.getAndSet(null); if (callback2 != null) { callback2.failed(cause); } else { asyncExecCallback.failed(cause); } } })); } else { callback1 = evaluateResponse(backendResponse1, responseDate1); } callbackRef.set(callback1); return callback1.handleResponse(backendResponse1, entityDetails); } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { final AsyncExecCallback callback1 = callbackRef.getAndSet(null); if (callback1 != null) { callback1.handleInformationResponse(response); } else { asyncExecCallback.handleInformationResponse(response); } } @Override public void completed() { final AsyncExecCallback callback1 = callbackRef.getAndSet(null); if (callback1 != null) { callback1.completed(); } else { asyncExecCallback.completed(); } } @Override public void failed(final Exception cause) { final AsyncExecCallback callback1 = callbackRef.getAndSet(null); if (callback1 != null) { callback1.failed(cause); } else { asyncExecCallback.failed(cause); } } }); } private void handleCacheMiss( final HttpHost target, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) { recordCacheMiss(target, request); if (mayCallBackend(request)) { final CancellableDependency operation = scope.cancellableDependency; operation.setDependency(responseCache.getVariantCacheEntriesWithEtags( target, request, new FutureCallback>() { @Override public void completed(final Map variants) { if (variants != null && !variants.isEmpty() && (entityProducer == null || entityProducer.isRepeatable())) { negotiateResponseFromVariants(target, request, entityProducer, scope, chain, asyncExecCallback, variants); } else { callBackend(target, request, entityProducer, scope, chain, asyncExecCallback); } } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } else { final SimpleHttpResponse cacheResponse = SimpleHttpResponse.create(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"); triggerResponse(cacheResponse, scope, asyncExecCallback); } } void negotiateResponseFromVariants( final HttpHost target, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback, final Map variants) { final CancellableDependency operation = scope.cancellableDependency; final HttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants( BasicRequestBuilder.copy(request).build(), variants); final Instant requestDate = getCurrentDate(); chainProceed(conditionalRequest, entityProducer, scope, chain, new AsyncExecCallback() { final AtomicReference callbackRef = new AtomicReference<>(); void updateVariantCacheEntry(final HttpResponse backendResponse, final Instant responseDate, final Variant matchingVariant) { recordCacheUpdate(scope.clientContext); operation.setDependency(responseCache.updateVariantCacheEntry( target, conditionalRequest, backendResponse, matchingVariant, requestDate, responseDate, new FutureCallback() { @Override public void completed(final HttpCacheEntry responseEntry) { if (shouldSendNotModifiedResponse(request, responseEntry)) { final SimpleHttpResponse cacheResponse = responseGenerator.generateNotModifiedResponse(responseEntry); triggerResponse(cacheResponse, scope, asyncExecCallback); } else { try { final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, responseEntry); operation.setDependency(responseCache.reuseVariantEntryFor( target, request, matchingVariant, new FutureCallback() { @Override public void completed(final Boolean result) { triggerResponse(cacheResponse, scope, asyncExecCallback); } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } catch (final ResourceIOException ex) { asyncExecCallback.failed(ex); } } } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } @Override public AsyncDataConsumer handleResponse( final HttpResponse backendResponse, final EntityDetails entityDetails) throws HttpException, IOException { final Instant responseDate = getCurrentDate(); backendResponse.addHeader("Via", generateViaHeader(backendResponse)); final AsyncExecCallback callback; if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) { callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback); } else { final Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG); if (resultEtagHeader == null) { LOG.warn("304 response did not contain ETag"); callback = new AsyncExecCallbackWrapper(asyncExecCallback, () -> callBackend(target, request, entityProducer, scope, chain, asyncExecCallback)); } else { final String resultEtag = resultEtagHeader.getValue(); final Variant matchingVariant = variants.get(resultEtag); if (matchingVariant == null) { LOG.debug("304 response did not contain ETag matching one sent in If-None-Match"); callback = new AsyncExecCallbackWrapper(asyncExecCallback, () -> callBackend(target, request, entityProducer, scope, chain, asyncExecCallback)); } else { if (revalidationResponseIsTooOld(backendResponse, matchingVariant.getEntry())) { final HttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest( BasicRequestBuilder.copy(request).build()); scope.clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, unconditional); callback = new AsyncExecCallbackWrapper(asyncExecCallback, () -> callBackend(target, request, entityProducer, scope, chain, asyncExecCallback)); } else { callback = new AsyncExecCallbackWrapper(asyncExecCallback, () -> updateVariantCacheEntry(backendResponse, responseDate, matchingVariant)); } } } } callbackRef.set(callback); return callback.handleResponse(backendResponse, entityDetails); } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { final AsyncExecCallback callback = callbackRef.getAndSet(null); if (callback != null) { callback.handleInformationResponse(response); } else { asyncExecCallback.handleInformationResponse(response); } } @Override public void completed() { final AsyncExecCallback callback = callbackRef.getAndSet(null); if (callback != null) { callback.completed(); } else { asyncExecCallback.completed(); } } @Override public void failed(final Exception cause) { final AsyncExecCallback callback = callbackRef.getAndSet(null); if (callback != null) { callback.failed(cause); } else { asyncExecCallback.failed(cause); } } }); } } BasicHttpAsyncCache.java000066400000000000000000000602141434266521000403130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheUpdateException; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.ComplexCancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.util.ByteArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class BasicHttpAsyncCache implements HttpAsyncCache { private static final Logger LOG = LoggerFactory.getLogger(BasicHttpAsyncCache.class); private final CacheUpdateHandler cacheUpdateHandler; private final CacheKeyGenerator cacheKeyGenerator; private final HttpAsyncCacheInvalidator cacheInvalidator; private final HttpAsyncCacheStorage storage; public BasicHttpAsyncCache( final ResourceFactory resourceFactory, final HttpAsyncCacheStorage storage, final CacheKeyGenerator cacheKeyGenerator, final HttpAsyncCacheInvalidator cacheInvalidator) { this.cacheUpdateHandler = new CacheUpdateHandler(resourceFactory); this.cacheKeyGenerator = cacheKeyGenerator; this.storage = storage; this.cacheInvalidator = cacheInvalidator; } public BasicHttpAsyncCache( final ResourceFactory resourceFactory, final HttpAsyncCacheStorage storage, final CacheKeyGenerator cacheKeyGenerator) { this(resourceFactory, storage, cacheKeyGenerator, DefaultAsyncCacheInvalidator.INSTANCE); } public BasicHttpAsyncCache(final ResourceFactory resourceFactory, final HttpAsyncCacheStorage storage) { this( resourceFactory, storage, CacheKeyGenerator.INSTANCE); } @Override public String generateKey(final HttpHost host, final HttpRequest request, final HttpCacheEntry cacheEntry) { if (cacheEntry == null) { return cacheKeyGenerator.generateKey(host, request); } else { return cacheKeyGenerator.generateKey(host, request, cacheEntry); } } @Override public Cancellable flushCacheEntriesFor( final HttpHost host, final HttpRequest request, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Flush cache entries: {}; {}", host, new RequestLine(request)); } if (!Method.isSafe(request.getMethod())) { final String cacheKey = cacheKeyGenerator.generateKey(host, request); return storage.removeEntry(cacheKey, new FutureCallback() { @Override public void completed(final Boolean result) { callback.completed(result); } @Override public void failed(final Exception ex) { if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error removing cache entry with key {}", cacheKey); } callback.completed(Boolean.TRUE); } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } }); } callback.completed(Boolean.TRUE); return Operations.nonCancellable(); } @Override public Cancellable flushCacheEntriesInvalidatedByRequest( final HttpHost host, final HttpRequest request, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Flush cache entries invalidated by request: {}; {}", host, new RequestLine(request)); } return cacheInvalidator.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyGenerator, storage, callback); } @Override public Cancellable flushCacheEntriesInvalidatedByExchange( final HttpHost host, final HttpRequest request, final HttpResponse response, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Flush cache entries invalidated by exchange: {}; {} -> {}", host, new RequestLine(request), new StatusLine(response)); } if (!Method.isSafe(request.getMethod())) { return cacheInvalidator.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyGenerator, storage, callback); } callback.completed(Boolean.TRUE); return Operations.nonCancellable(); } Cancellable storeInCache( final String cacheKey, final HttpHost host, final HttpRequest request, final HttpCacheEntry entry, final FutureCallback callback) { if (entry.hasVariants()) { return storeVariantEntry(cacheKey, host, request, entry, callback); } else { return storeEntry(cacheKey, entry, callback); } } Cancellable storeEntry( final String cacheKey, final HttpCacheEntry entry, final FutureCallback callback) { return storage.putEntry(cacheKey, entry, new FutureCallback() { @Override public void completed(final Boolean result) { callback.completed(result); } @Override public void failed(final Exception ex) { if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error storing cache entry with key {}", cacheKey); } callback.completed(Boolean.TRUE); } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } }); } Cancellable storeVariantEntry( final String cacheKey, final HttpHost host, final HttpRequest req, final HttpCacheEntry entry, final FutureCallback callback) { final String variantKey = cacheKeyGenerator.generateVariantKey(req, entry); final String variantCacheKey = cacheKeyGenerator.generateKey(host, req, entry); return storage.putEntry(variantCacheKey, entry, new FutureCallback() { @Override public void completed(final Boolean result) { storage.updateEntry(cacheKey, existing -> cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantCacheKey), new FutureCallback() { @Override public void completed(final Boolean result) { callback.completed(result); } @Override public void failed(final Exception ex) { if (ex instanceof HttpCacheUpdateException) { if (LOG.isWarnEnabled()) { LOG.warn("Cannot update cache entry with key {}", cacheKey); } } else if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } }); } @Override public void failed(final Exception ex) { if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", variantCacheKey); } callback.completed(Boolean.TRUE); } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } }); } @Override public Cancellable reuseVariantEntryFor( final HttpHost host, final HttpRequest request, final Variant variant, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Re-use variant entry: {}; {} / {}", host, new RequestLine(request), variant); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); final HttpCacheEntry entry = variant.getEntry(); final String variantKey = cacheKeyGenerator.generateVariantKey(request, entry); final String variantCacheKey = variant.getCacheKey(); return storage.updateEntry(cacheKey, existing -> cacheUpdateHandler.updateParentCacheEntry(request.getRequestUri(), existing, entry, variantKey, variantCacheKey), new FutureCallback() { @Override public void completed(final Boolean result) { callback.completed(result); } @Override public void failed(final Exception ex) { if (ex instanceof HttpCacheUpdateException) { if (LOG.isWarnEnabled()) { LOG.warn("Cannot update cache entry with key {}", cacheKey); } } else if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } }); } @Override public Cancellable updateCacheEntry( final HttpHost host, final HttpRequest request, final HttpCacheEntry stale, final HttpResponse originResponse, final Instant requestSent, final Instant responseReceived, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Update cache entry: {}; {}", host, new RequestLine(request)); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); try { final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry( request.getRequestUri(), stale, requestSent, responseReceived, originResponse); return storeInCache(cacheKey, host, request, updatedEntry, new FutureCallback() { @Override public void completed(final Boolean result) { callback.completed(updatedEntry); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } callback.completed(stale); return Operations.nonCancellable(); } } @Override public Cancellable updateVariantCacheEntry( final HttpHost host, final HttpRequest request, final HttpResponse originResponse, final Variant variant, final Instant requestSent, final Instant responseReceived, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Update variant cache entry: {}; {} / {}", host, new RequestLine(request), variant); } final HttpCacheEntry entry = variant.getEntry(); final String cacheKey = variant.getCacheKey(); try { final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry( request.getRequestUri(), entry, requestSent, responseReceived, originResponse); return storeEntry(cacheKey, updatedEntry, new FutureCallback() { @Override public void completed(final Boolean result) { callback.completed(updatedEntry); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } callback.completed(entry); return Operations.nonCancellable(); } } @Override public Cancellable createCacheEntry( final HttpHost host, final HttpRequest request, final HttpResponse originResponse, final ByteArrayBuffer content, final Instant requestSent, final Instant responseReceived, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Create cache entry: {}; {}", host, new RequestLine(request)); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); try { final HttpCacheEntry entry = cacheUpdateHandler.createCacheEntry(request, originResponse, content, requestSent, responseReceived); return storeInCache(cacheKey, host, request, entry, new FutureCallback() { @Override public void completed(final Boolean result) { callback.completed(entry); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error creating cache entry with key {}", cacheKey); } callback.completed(new HttpCacheEntry( requestSent, responseReceived, originResponse.getCode(), originResponse.getHeaders(), content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null)); return Operations.nonCancellable(); } } @Override public Cancellable getCacheEntry(final HttpHost host, final HttpRequest request, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("Get cache entry: {}; {}", host, new RequestLine(request)); } final ComplexCancellable complexCancellable = new ComplexCancellable(); final String cacheKey = cacheKeyGenerator.generateKey(host, request); complexCancellable.setDependency(storage.getEntry(cacheKey, new FutureCallback() { @Override public void completed(final HttpCacheEntry root) { if (root != null) { if (root.hasVariants()) { final String variantKey = cacheKeyGenerator.generateVariantKey(request, root); final String variantCacheKey = root.getVariantMap().get(variantKey); if (variantCacheKey != null) { complexCancellable.setDependency(storage.getEntry( variantCacheKey, new FutureCallback() { @Override public void completed(final HttpCacheEntry result) { callback.completed(result); } @Override public void failed(final Exception ex) { if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with key {}", variantCacheKey); } callback.completed(null); } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } })); return; } } } callback.completed(root); } @Override public void failed(final Exception ex) { if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with key {}", cacheKey); } callback.completed(null); } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } })); return complexCancellable; } @Override public Cancellable getVariantCacheEntriesWithEtags( final HttpHost host, final HttpRequest request, final FutureCallback> callback) { if (LOG.isDebugEnabled()) { LOG.debug("Get variant cache entries: {}; {}", host, new RequestLine(request)); } final ComplexCancellable complexCancellable = new ComplexCancellable(); final String cacheKey = cacheKeyGenerator.generateKey(host, request); final Map variants = new HashMap<>(); complexCancellable.setDependency(storage.getEntry(cacheKey, new FutureCallback() { @Override public void completed(final HttpCacheEntry rootEntry) { if (rootEntry != null && rootEntry.hasVariants()) { final Set variantCacheKeys = rootEntry.getVariantMap().keySet(); complexCancellable.setDependency(storage.getEntries( variantCacheKeys, new FutureCallback>() { @Override public void completed(final Map resultMap) { for (final Map.Entry resultMapEntry : resultMap.entrySet()) { final String cacheKey = resultMapEntry.getKey(); final HttpCacheEntry cacheEntry = resultMapEntry.getValue(); final Header etagHeader = cacheEntry.getFirstHeader(HeaderConstants.ETAG); if (etagHeader != null) { variants.put(etagHeader.getValue(), new Variant(cacheKey, cacheEntry)); } } callback.completed(variants); } @Override public void failed(final Exception ex) { if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with keys {}", variantCacheKeys); } callback.completed(variants); } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } })); } else { callback.completed(variants); } } @Override public void failed(final Exception ex) { if (ex instanceof ResourceIOException) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with key {}", cacheKey); } callback.completed(variants); } else { callback.failed(ex); } } @Override public void cancelled() { callback.cancelled(); } })); return complexCancellable; } } BasicHttpCache.java000066400000000000000000000350171434266521000373200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheInvalidator; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheUpdateException; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.util.ByteArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class BasicHttpCache implements HttpCache { private static final Logger LOG = LoggerFactory.getLogger(BasicHttpCache.class); private final CacheUpdateHandler cacheUpdateHandler; private final CacheKeyGenerator cacheKeyGenerator; private final HttpCacheInvalidator cacheInvalidator; private final HttpCacheStorage storage; public BasicHttpCache( final ResourceFactory resourceFactory, final HttpCacheStorage storage, final CacheKeyGenerator cacheKeyGenerator, final HttpCacheInvalidator cacheInvalidator) { this.cacheUpdateHandler = new CacheUpdateHandler(resourceFactory); this.cacheKeyGenerator = cacheKeyGenerator; this.storage = storage; this.cacheInvalidator = cacheInvalidator; } public BasicHttpCache( final ResourceFactory resourceFactory, final HttpCacheStorage storage, final CacheKeyGenerator cacheKeyGenerator) { this(resourceFactory, storage, cacheKeyGenerator, new DefaultCacheInvalidator()); } public BasicHttpCache(final ResourceFactory resourceFactory, final HttpCacheStorage storage) { this( resourceFactory, storage, new CacheKeyGenerator()); } public BasicHttpCache(final CacheConfig config) { this(new HeapResourceFactory(), new BasicHttpCacheStorage(config)); } public BasicHttpCache() { this(CacheConfig.DEFAULT); } @Override public String generateKey(final HttpHost host, final HttpRequest request, final HttpCacheEntry cacheEntry) { if (cacheEntry == null) { return cacheKeyGenerator.generateKey(host, request); } else { return cacheKeyGenerator.generateKey(host, request, cacheEntry); } } @Override public void flushCacheEntriesFor(final HttpHost host, final HttpRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("Flush cache entries: {}; {}", host, new RequestLine(request)); } if (!Method.isSafe(request.getMethod())) { final String cacheKey = cacheKeyGenerator.generateKey(host, request); try { storage.removeEntry(cacheKey); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error removing cache entry with key {}", cacheKey); } } } } @Override public void flushCacheEntriesInvalidatedByRequest(final HttpHost host, final HttpRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("Flush cache entries invalidated by request: {}; {}", host, new RequestLine(request)); } cacheInvalidator.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyGenerator, storage); } @Override public void flushCacheEntriesInvalidatedByExchange(final HttpHost host, final HttpRequest request, final HttpResponse response) { if (LOG.isDebugEnabled()) { LOG.debug("Flush cache entries invalidated by exchange: {}; {} -> {}", host, new RequestLine(request), new StatusLine(response)); } if (!Method.isSafe(request.getMethod())) { cacheInvalidator.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyGenerator, storage); } } void storeInCache( final String cacheKey, final HttpHost host, final HttpRequest request, final HttpCacheEntry entry) { if (entry.hasVariants()) { storeVariantEntry(cacheKey, host, request, entry); } else { storeEntry(cacheKey, entry); } } void storeEntry(final String cacheKey, final HttpCacheEntry entry) { try { storage.putEntry(cacheKey, entry); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error storing cache entry with key {}", cacheKey); } } } void storeVariantEntry( final String cacheKey, final HttpHost host, final HttpRequest req, final HttpCacheEntry entry) { final String variantKey = cacheKeyGenerator.generateVariantKey(req, entry); final String variantCacheKey = cacheKeyGenerator.generateKey(host, req, entry); storeEntry(variantCacheKey, entry); try { storage.updateEntry(cacheKey, existing -> cacheUpdateHandler.updateParentCacheEntry(req.getRequestUri(), existing, entry, variantKey, variantCacheKey)); } catch (final HttpCacheUpdateException ex) { if (LOG.isWarnEnabled()) { LOG.warn("Cannot update cache entry with key {}", cacheKey); } } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } } } @Override public void reuseVariantEntryFor( final HttpHost host, final HttpRequest request, final Variant variant) { if (LOG.isDebugEnabled()) { LOG.debug("Re-use variant entry: {}; {} / {}", host, new RequestLine(request), variant); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); final HttpCacheEntry entry = variant.getEntry(); final String variantKey = cacheKeyGenerator.generateVariantKey(request, entry); final String variantCacheKey = variant.getCacheKey(); try { storage.updateEntry(cacheKey, existing -> cacheUpdateHandler.updateParentCacheEntry(request.getRequestUri(), existing, entry, variantKey, variantCacheKey)); } catch (final HttpCacheUpdateException ex) { if (LOG.isWarnEnabled()) { LOG.warn("Cannot update cache entry with key {}", cacheKey); } } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } } } @Override public HttpCacheEntry updateCacheEntry( final HttpHost host, final HttpRequest request, final HttpCacheEntry stale, final HttpResponse originResponse, final Instant requestSent, final Instant responseReceived) { if (LOG.isDebugEnabled()) { LOG.debug("Update cache entry: {}; {}", host, new RequestLine(request)); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); try { final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry( request.getRequestUri(), stale, requestSent, responseReceived, originResponse); storeInCache(cacheKey, host, request, updatedEntry); return updatedEntry; } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } return stale; } } @Override public HttpCacheEntry updateVariantCacheEntry( final HttpHost host, final HttpRequest request, final HttpResponse originResponse, final Variant variant, final Instant requestSent, final Instant responseReceived) { if (LOG.isDebugEnabled()) { LOG.debug("Update variant cache entry: {}; {} / {}", host, new RequestLine(request), variant); } final HttpCacheEntry entry = variant.getEntry(); final String cacheKey = variant.getCacheKey(); try { final HttpCacheEntry updatedEntry = cacheUpdateHandler.updateCacheEntry( request.getRequestUri(), entry, requestSent, responseReceived, originResponse); storeEntry(cacheKey, updatedEntry); return updatedEntry; } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error updating cache entry with key {}", cacheKey); } return entry; } } @Override public HttpCacheEntry createCacheEntry( final HttpHost host, final HttpRequest request, final HttpResponse originResponse, final ByteArrayBuffer content, final Instant requestSent, final Instant responseReceived) { if (LOG.isDebugEnabled()) { LOG.debug("Create cache entry: {}; {}", host, new RequestLine(request)); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); try { final HttpCacheEntry entry = cacheUpdateHandler.createCacheEntry(request, originResponse, content, requestSent, responseReceived); storeInCache(cacheKey, host, request, entry); return entry; } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error creating cache entry with key {}", cacheKey); } return new HttpCacheEntry( requestSent, responseReceived, originResponse.getCode(), originResponse.getHeaders(), content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null); } } @Override public HttpCacheEntry getCacheEntry(final HttpHost host, final HttpRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("Get cache entry: {}; {}", host, new RequestLine(request)); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); final HttpCacheEntry root; try { root = storage.getEntry(cacheKey); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with key {}", cacheKey); } return null; } if (root == null) { return null; } if (!root.hasVariants()) { return root; } final String variantKey = cacheKeyGenerator.generateVariantKey(request, root); final String variantCacheKey = root.getVariantMap().get(variantKey); if (variantCacheKey == null) { return null; } try { return storage.getEntry(variantCacheKey); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with key {}", variantCacheKey); } return null; } } @Override public Map getVariantCacheEntriesWithEtags(final HttpHost host, final HttpRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("Get variant cache entries: {}; {}", host, new RequestLine(request)); } final Map variants = new HashMap<>(); final String cacheKey = cacheKeyGenerator.generateKey(host, request); final HttpCacheEntry root; try { root = storage.getEntry(cacheKey); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with key {}", cacheKey); } return variants; } if (root != null && root.hasVariants()) { for(final Map.Entry variant : root.getVariantMap().entrySet()) { final String variantCacheKey = variant.getValue(); try { final HttpCacheEntry entry = storage.getEntry(variantCacheKey); if (entry != null) { final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); if (etagHeader != null) { variants.put(etagHeader.getValue(), new Variant(variantCacheKey, entry)); } } } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("I/O error retrieving cache entry with key {}", variantCacheKey); } return variants; } } } return variants; } } BasicHttpCacheStorage.java000066400000000000000000000101551434266521000406410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheCASOperation; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Basic {@link HttpCacheStorage} implementation backed by an instance of * {@link java.util.LinkedHashMap}. In other words, cache entries and * the cached response bodies are held in-memory. This cache does NOT * deallocate resources associated with the cache entries; it is intended * for use with {@link HeapResource} and similar. This is the default cache * storage backend used by {@link CachingHttpClients}. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.SAFE) public class BasicHttpCacheStorage implements HttpCacheStorage { private final CacheMap entries; public BasicHttpCacheStorage(final CacheConfig config) { super(); this.entries = new CacheMap(config.getMaxCacheEntries()); } /** * Places a HttpCacheEntry in the cache * * @param url * Url to use as the cache key * @param entry * HttpCacheEntry to place in the cache */ @Override public synchronized void putEntry( final String url, final HttpCacheEntry entry) throws ResourceIOException { entries.put(url, entry); } /** * Gets an entry from the cache, if it exists * * @param url * Url that is the cache key * @return HttpCacheEntry if one exists, or null for cache miss */ @Override public synchronized HttpCacheEntry getEntry(final String url) throws ResourceIOException { return entries.get(url); } /** * Removes a HttpCacheEntry from the cache * * @param url * Url that is the cache key */ @Override public synchronized void removeEntry(final String url) throws ResourceIOException { entries.remove(url); } @Override public synchronized void updateEntry( final String url, final HttpCacheCASOperation casOperation) throws ResourceIOException { final HttpCacheEntry existingEntry = entries.get(url); entries.put(url, casOperation.execute(existingEntry)); } @Override public Map getEntries(final Collection keys) throws ResourceIOException { Args.notNull(keys, "Key"); final Map resultMap = new HashMap<>(keys.size()); for (final String key: keys) { final HttpCacheEntry entry = getEntry(key); if (entry != null) { resultMap.put(key, entry); } } return resultMap; } } BasicIdGenerator.java000066400000000000000000000052441434266521000376570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Formatter; import java.util.Locale; /** * Should produce reasonably unique tokens. */ class BasicIdGenerator { private final String hostname; private final SecureRandom rnd; private long count; public BasicIdGenerator() { super(); String hostname; try { hostname = InetAddress.getLocalHost().getHostName(); } catch (final UnknownHostException ex) { hostname = "localhost"; } this.hostname = hostname; try { this.rnd = SecureRandom.getInstance("SHA1PRNG"); } catch (final NoSuchAlgorithmException ex) { throw new Error(ex); } this.rnd.setSeed(System.currentTimeMillis()); } public synchronized void generate(final StringBuilder buffer) { this.count++; final int rndnum = this.rnd.nextInt(); buffer.append(System.currentTimeMillis()); buffer.append('.'); try (Formatter formatter = new Formatter(buffer, Locale.ROOT)) { formatter.format("%1$016x-%2$08x", this.count, rndnum); } buffer.append('.'); buffer.append(this.hostname); } public String generate() { final StringBuilder buffer = new StringBuilder(); generate(buffer); return buffer.toString(); } } ByteArrayCacheEntrySerializer.java000066400000000000000000000121731434266521000424130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * {@link HttpCacheEntrySerializer} implementation that uses the default (native) * serialization. * * @see java.io.Serializable * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public final class ByteArrayCacheEntrySerializer implements HttpCacheEntrySerializer { public static final ByteArrayCacheEntrySerializer INSTANCE = new ByteArrayCacheEntrySerializer(); @Override public byte[] serialize(final HttpCacheStorageEntry cacheEntry) throws ResourceIOException { if (cacheEntry == null) { return null; } final ByteArrayOutputStream buf = new ByteArrayOutputStream(); try (final ObjectOutputStream oos = new ObjectOutputStream(buf)) { oos.writeObject(cacheEntry); } catch (final IOException ex) { throw new ResourceIOException(ex.getMessage(), ex); } return buf.toByteArray(); } @Override public HttpCacheStorageEntry deserialize(final byte[] serializedObject) throws ResourceIOException { if (serializedObject == null) { return null; } try (final ObjectInputStream ois = new RestrictedObjectInputStream(new ByteArrayInputStream(serializedObject))) { return (HttpCacheStorageEntry) ois.readObject(); } catch (final IOException | ClassNotFoundException ex) { throw new ResourceIOException(ex.getMessage(), ex); } } // visible for testing static class RestrictedObjectInputStream extends ObjectInputStream { private static final List ALLOWED_CLASS_PATTERNS = Collections.unmodifiableList(Arrays.asList( Pattern.compile("^(\\[L)?org\\.apache\\.hc\\.(.*)"), Pattern.compile("^(?:\\[+L)?java\\.util\\..*$"), Pattern.compile("^(?:\\[+L)?java\\.lang\\..*$"), Pattern.compile("^(?:\\[+L)?java\\.time\\..*$"), // java 8 time Pattern.compile("^\\[+Z$"), // boolean Pattern.compile("^\\[+B$"), // byte Pattern.compile("^\\[+C$"), // char Pattern.compile("^\\[+D$"), // double Pattern.compile("^\\[+F$"), // float Pattern.compile("^\\[+I$"), // int Pattern.compile("^\\[+J$"), // long Pattern.compile("^\\[+S$") // short )); private RestrictedObjectInputStream(final InputStream in) throws IOException { super(in); } @Override protected Class resolveClass(final ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException { final String className = objectStreamClass.getName(); if (!isAllowedClassName(className)) { throw new ResourceIOException(String.format( "Class %s is not allowed for deserialization", objectStreamClass.getName())); } return super.resolveClass(objectStreamClass); } // visible for testing static boolean isAllowedClassName(final String className) { for (final Pattern allowedClassPattern : ALLOWED_CLASS_PATTERNS) { if (allowedClassPattern.matcher(className).matches()) { return true; } } return false; } } } CacheConfig.java000066400000000000000000000507411434266521000366450ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** *

Java Beans-style configuration for caching {@link org.apache.hc.client5.http.classic.HttpClient}. * Any class in the caching module that has configuration options should take a * {@link CacheConfig} argument in one of its constructors. A * {@code CacheConfig} instance has sane and conservative defaults, so the * easiest way to specify options is to get an instance and then set just * the options you want to modify from their defaults.

* *

N.B. This class is only for caching-specific configuration; to * configure the behavior of the rest of the client, configure the * {@link org.apache.hc.client5.http.classic.HttpClient} used as the "backend" * for the {@code CachingHttpClient}.

* *

Cache configuration can be grouped into the following categories:

* *

Cache size. If the backend storage supports these limits, you * can specify the {@link CacheConfig#getMaxCacheEntries maximum number of * cache entries} as well as the {@link CacheConfig#getMaxObjectSize()} * maximum cacheable response body size}.

* *

Public/private caching. By default, the caching module considers * itself to be a shared (public) cache, and will not, for example, cache * responses to requests with {@code Authorization} headers or responses * marked with {@code Cache-Control: private}. If, however, the cache * is only going to be used by one logical "user" (behaving similarly to a * browser cache), then you will want to {@link * CacheConfig#isSharedCache()} turn off the shared cache setting}.

* *

303 caching. RFC2616 explicitly disallows caching 303 responses; * however, the HTTPbis working group says they can be cached * if explicitly indicated in the response headers and permitted by the request method. * (They also indicate that disallowing 303 caching is actually an unintended * spec error in RFC2616). * This behavior is off by default, to err on the side of a conservative * adherence to the existing standard, but you may want to * {@link Builder#setAllow303Caching(boolean) enable it}. * *

Weak ETags on PUT/DELETE If-Match requests. RFC2616 explicitly * prohibits the use of weak validators in non-GET requests, however, the * HTTPbis working group says while the limitation for weak validators on ranged * requests makes sense, weak ETag validation is useful on full non-GET * requests; e.g., PUT with If-Match. This behavior is off by default, to err on * the side of a conservative adherence to the existing standard, but you may * want to {@link Builder#setWeakETagOnPutDeleteAllowed(boolean) enable it}. * *

Heuristic caching. Per RFC2616, a cache may cache certain cache * entries even if no explicit cache control headers are set by the origin. * This behavior is off by default, but you may want to turn this on if you * are working with an origin that doesn't set proper headers but where you * still want to cache the responses. You will want to {@link * CacheConfig#isHeuristicCachingEnabled()} enable heuristic caching}, * then specify either a {@link CacheConfig#getHeuristicDefaultLifetime() * default freshness lifetime} and/or a {@link * CacheConfig#getHeuristicCoefficient() fraction of the time since * the resource was last modified}. See Sections * * 13.2.2 and * 13.2.4 of the HTTP/1.1 RFC for more details on heuristic caching.

* *

Background validation. The cache module supports the * {@code stale-while-revalidate} directive of * RFC5861, which allows * certain cache entry revalidations to happen in the background. Asynchronous * validation is enabled by default but it could be disabled by setting the number * of re-validation workers to {@code 0} with {@link CacheConfig#getAsynchronousWorkers()} * parameter

*/ public class CacheConfig implements Cloneable { /** Default setting for the maximum object size that will be * cached, in bytes. */ public final static int DEFAULT_MAX_OBJECT_SIZE_BYTES = 8192; /** Default setting for the maximum number of cache entries * that will be retained. */ public final static int DEFAULT_MAX_CACHE_ENTRIES = 1000; /** Default setting for the number of retries on a failed * cache processChallenge */ public final static int DEFAULT_MAX_UPDATE_RETRIES = 1; /** Default setting for 303 caching */ public final static boolean DEFAULT_303_CACHING_ENABLED = false; /** Default setting to allow weak tags on PUT/DELETE methods */ public final static boolean DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED = false; /** Default setting for heuristic caching */ public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false; /** Default coefficient used to heuristically determine freshness * lifetime from the Last-Modified time of a cache entry. */ public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f; /** Default lifetime to be assumed when we cannot calculate * freshness heuristically. */ public final static TimeValue DEFAULT_HEURISTIC_LIFETIME = TimeValue.ZERO_MILLISECONDS; /** Default number of worker threads to allow for background revalidations * resulting from the stale-while-revalidate directive. */ public static final int DEFAULT_ASYNCHRONOUS_WORKERS = 1; public static final CacheConfig DEFAULT = new Builder().build(); private final long maxObjectSize; private final int maxCacheEntries; private final int maxUpdateRetries; private final boolean allow303Caching; private final boolean weakETagOnPutDeleteAllowed; private final boolean heuristicCachingEnabled; private final float heuristicCoefficient; private final TimeValue heuristicDefaultLifetime; private final boolean sharedCache; private final boolean freshnessCheckEnabled; private final int asynchronousWorkers; private final boolean neverCacheHTTP10ResponsesWithQuery; CacheConfig( final long maxObjectSize, final int maxCacheEntries, final int maxUpdateRetries, final boolean allow303Caching, final boolean weakETagOnPutDeleteAllowed, final boolean heuristicCachingEnabled, final float heuristicCoefficient, final TimeValue heuristicDefaultLifetime, final boolean sharedCache, final boolean freshnessCheckEnabled, final int asynchronousWorkers, final boolean neverCacheHTTP10ResponsesWithQuery) { super(); this.maxObjectSize = maxObjectSize; this.maxCacheEntries = maxCacheEntries; this.maxUpdateRetries = maxUpdateRetries; this.allow303Caching = allow303Caching; this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed; this.heuristicCachingEnabled = heuristicCachingEnabled; this.heuristicCoefficient = heuristicCoefficient; this.heuristicDefaultLifetime = heuristicDefaultLifetime; this.sharedCache = sharedCache; this.freshnessCheckEnabled = freshnessCheckEnabled; this.asynchronousWorkers = asynchronousWorkers; this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery; } /** * Returns the current maximum response body size that will be cached. * @return size in bytes * * @since 4.2 */ public long getMaxObjectSize() { return maxObjectSize; } /** * Returns whether the cache will never cache HTTP 1.0 responses with a query string or not. * @return {@code true} to not cache query string responses, {@code false} to cache if explicit cache headers are * found */ public boolean isNeverCacheHTTP10ResponsesWithQuery() { return neverCacheHTTP10ResponsesWithQuery; } /** * Returns the maximum number of cache entries the cache will retain. */ public int getMaxCacheEntries() { return maxCacheEntries; } /** * Returns the number of times to retry a cache processChallenge on failure */ public int getMaxUpdateRetries(){ return maxUpdateRetries; } /** * Returns whether 303 caching is enabled. * @return {@code true} if it is enabled. */ public boolean is303CachingEnabled() { return allow303Caching; } /** * Returns whether weak etags is allowed with PUT/DELETE methods. * @return {@code true} if it is allowed. */ public boolean isWeakETagOnPutDeleteAllowed() { return weakETagOnPutDeleteAllowed; } /** * Returns whether heuristic caching is enabled. * @return {@code true} if it is enabled. */ public boolean isHeuristicCachingEnabled() { return heuristicCachingEnabled; } /** * Returns lifetime coefficient used in heuristic freshness caching. */ public float getHeuristicCoefficient() { return heuristicCoefficient; } /** * Get the default lifetime to be used if heuristic freshness calculation is * not possible. */ public TimeValue getHeuristicDefaultLifetime() { return heuristicDefaultLifetime; } /** * Returns whether the cache will behave as a shared cache or not. * @return {@code true} for a shared cache, {@code false} for a non- * shared (private) cache */ public boolean isSharedCache() { return sharedCache; } /** * Returns whether the cache will perform an extra cache entry freshness check * upon cache update in case of a cache miss * * @since 5.0 */ public boolean isFreshnessCheckEnabled() { return freshnessCheckEnabled; } /** * Returns the maximum number of threads to allow for background * revalidations due to the {@code stale-while-revalidate} directive. A * value of 0 means background revalidations are disabled. */ public int getAsynchronousWorkers() { return asynchronousWorkers; } @Override protected CacheConfig clone() throws CloneNotSupportedException { return (CacheConfig) super.clone(); } public static Builder custom() { return new Builder(); } public static Builder copy(final CacheConfig config) { Args.notNull(config, "Cache config"); return new Builder() .setMaxObjectSize(config.getMaxObjectSize()) .setMaxCacheEntries(config.getMaxCacheEntries()) .setMaxUpdateRetries(config.getMaxUpdateRetries()) .setHeuristicCachingEnabled(config.isHeuristicCachingEnabled()) .setHeuristicCoefficient(config.getHeuristicCoefficient()) .setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime()) .setSharedCache(config.isSharedCache()) .setAsynchronousWorkers(config.getAsynchronousWorkers()) .setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery()); } public static class Builder { private long maxObjectSize; private int maxCacheEntries; private int maxUpdateRetries; private boolean allow303Caching; private boolean weakETagOnPutDeleteAllowed; private boolean heuristicCachingEnabled; private float heuristicCoefficient; private TimeValue heuristicDefaultLifetime; private boolean sharedCache; private boolean freshnessCheckEnabled; private int asynchronousWorkers; private boolean neverCacheHTTP10ResponsesWithQuery; Builder() { this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES; this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES; this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES; this.allow303Caching = DEFAULT_303_CACHING_ENABLED; this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED; this.heuristicCachingEnabled = DEFAULT_HEURISTIC_CACHING_ENABLED; this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT; this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME; this.sharedCache = true; this.freshnessCheckEnabled = true; this.asynchronousWorkers = DEFAULT_ASYNCHRONOUS_WORKERS; } /** * Specifies the maximum response body size that will be eligible for caching. * @param maxObjectSize size in bytes */ public Builder setMaxObjectSize(final long maxObjectSize) { this.maxObjectSize = maxObjectSize; return this; } /** * Sets the maximum number of cache entries the cache will retain. */ public Builder setMaxCacheEntries(final int maxCacheEntries) { this.maxCacheEntries = maxCacheEntries; return this; } /** * Sets the number of times to retry a cache processChallenge on failure */ public Builder setMaxUpdateRetries(final int maxUpdateRetries) { this.maxUpdateRetries = maxUpdateRetries; return this; } /** * Enables or disables 303 caching. * @param allow303Caching should be {@code true} to * permit 303 caching, {@code false} to disable it. */ public Builder setAllow303Caching(final boolean allow303Caching) { this.allow303Caching = allow303Caching; return this; } /** * Allows or disallows weak etags to be used with PUT/DELETE If-Match requests. * @param weakETagOnPutDeleteAllowed should be {@code true} to * permit weak etags, {@code false} to reject them. */ public Builder setWeakETagOnPutDeleteAllowed(final boolean weakETagOnPutDeleteAllowed) { this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed; return this; } /** * Enables or disables heuristic caching. * @param heuristicCachingEnabled should be {@code true} to * permit heuristic caching, {@code false} to enable it. */ public Builder setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) { this.heuristicCachingEnabled = heuristicCachingEnabled; return this; } /** * Sets coefficient to be used in heuristic freshness caching. This is * interpreted as the fraction of the time between the {@code Last-Modified} * and {@code Date} headers of a cached response during which the cached * response will be considered heuristically fresh. * @param heuristicCoefficient should be between {@code 0.0} and * {@code 1.0}. */ public Builder setHeuristicCoefficient(final float heuristicCoefficient) { this.heuristicCoefficient = heuristicCoefficient; return this; } /** * Sets default lifetime to be used if heuristic freshness calculation * is not possible. Explicit cache control directives on either the * request or origin response will override this, as will the heuristic * {@code Last-Modified} freshness calculation if it is available. * * @param heuristicDefaultLifetime is the number to consider a * cache-eligible response fresh in the absence of other information. * Set this to {@code 0} to disable this style of heuristic caching. */ public Builder setHeuristicDefaultLifetime(final TimeValue heuristicDefaultLifetime) { this.heuristicDefaultLifetime = heuristicDefaultLifetime; return this; } /** * Sets whether the cache should behave as a shared cache or not. * @param sharedCache true to behave as a shared cache, false to * behave as a non-shared (private) cache. To have the cache * behave like a browser cache, you want to set this to {@code false}. */ public Builder setSharedCache(final boolean sharedCache) { this.sharedCache = sharedCache; return this; } /** * Sets the maximum number of threads to allow for background * revalidations due to the {@code stale-while-revalidate} directive. * @param asynchronousWorkers number of threads; a value of 0 disables background * revalidations. */ public Builder setAsynchronousWorkers(final int asynchronousWorkers) { this.asynchronousWorkers = asynchronousWorkers; return this; } /** * Sets whether the cache should never cache HTTP 1.0 responses with a query string or not. * @param neverCacheHTTP10ResponsesWithQuery true to never cache responses with a query * string, false to cache if explicit cache headers are found. Set this to {@code true} * to better emulate IE, which also never caches responses, regardless of what caching * headers may be present. */ public Builder setNeverCacheHTTP10ResponsesWithQueryString( final boolean neverCacheHTTP10ResponsesWithQuery) { this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery; return this; } public Builder setFreshnessCheckEnabled(final boolean freshnessCheckEnabled) { this.freshnessCheckEnabled = freshnessCheckEnabled; return this; } public CacheConfig build() { return new CacheConfig( maxObjectSize, maxCacheEntries, maxUpdateRetries, allow303Caching, weakETagOnPutDeleteAllowed, heuristicCachingEnabled, heuristicCoefficient, heuristicDefaultLifetime, sharedCache, freshnessCheckEnabled, asynchronousWorkers, neverCacheHTTP10ResponsesWithQuery); } } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[maxObjectSize=").append(this.maxObjectSize) .append(", maxCacheEntries=").append(this.maxCacheEntries) .append(", maxUpdateRetries=").append(this.maxUpdateRetries) .append(", 303CachingEnabled=").append(this.allow303Caching) .append(", weakETagOnPutDeleteAllowed=").append(this.weakETagOnPutDeleteAllowed) .append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled) .append(", heuristicCoefficient=").append(this.heuristicCoefficient) .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime) .append(", sharedCache=").append(this.sharedCache) .append(", freshnessCheckEnabled=").append(this.freshnessCheckEnabled) .append(", asynchronousWorkers=").append(this.asynchronousWorkers) .append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery) .append("]"); return builder.toString(); } } CacheInvalidatorBase.java000066400000000000000000000102141434266521000404760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.net.URI; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; class CacheInvalidatorBase { static boolean shouldInvalidateHeadCacheEntry(final HttpRequest req, final HttpCacheEntry parentCacheEntry) { return requestIsGet(req) && isAHeadCacheEntry(parentCacheEntry); } static boolean requestIsGet(final HttpRequest req) { return req.getMethod().equals((HeaderConstants.GET_METHOD)); } static boolean isAHeadCacheEntry(final HttpCacheEntry parentCacheEntry) { return parentCacheEntry != null && parentCacheEntry.getRequestMethod().equals(HeaderConstants.HEAD_METHOD); } static boolean isSameHost(final URI requestURI, final URI targetURI) { return targetURI.isAbsolute() && targetURI.getAuthority().equalsIgnoreCase(requestURI.getAuthority()); } static boolean requestShouldNotBeCached(final HttpRequest req) { final String method = req.getMethod(); return notGetOrHeadRequest(method); } static boolean notGetOrHeadRequest(final String method) { return !(HeaderConstants.GET_METHOD.equals(method) || HeaderConstants.HEAD_METHOD.equals(method)); } private static URI getLocationURI(final URI requestUri, final HttpResponse response, final String headerName) { final Header h = response.getFirstHeader(headerName); if (h == null) { return null; } final URI locationUri = HttpCacheSupport.normalizeQuietly(h.getValue()); if (locationUri == null) { return requestUri; } if (locationUri.isAbsolute()) { return locationUri; } else { return URIUtils.resolve(requestUri, locationUri); } } static URI getContentLocationURI(final URI requestUri, final HttpResponse response) { return getLocationURI(requestUri, response, HttpHeaders.CONTENT_LOCATION); } static URI getLocationURI(final URI requestUri, final HttpResponse response) { return getLocationURI(requestUri, response, HttpHeaders.LOCATION); } static boolean responseAndEntryEtagsDiffer(final HttpResponse response, final HttpCacheEntry entry) { final Header entryEtag = entry.getFirstHeader(HeaderConstants.ETAG); final Header responseEtag = response.getFirstHeader(HeaderConstants.ETAG); if (entryEtag == null || responseEtag == null) { return false; } return (!entryEtag.getValue().equals(responseEtag.getValue())); } static boolean responseDateOlderThanEntryDate(final HttpResponse response, final HttpCacheEntry entry) { return DateSupport.isBefore(response, entry, HttpHeaders.DATE); } } CacheKeyGenerator.java000066400000000000000000000142311434266521000400310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.MessageSupport; /** * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public class CacheKeyGenerator implements Resolver { public static final CacheKeyGenerator INSTANCE = new CacheKeyGenerator(); @Override public String resolve(final URI uri) { return generateKey(uri); } /** * Computes a key for the given request {@link URI} that can be used as * a unique identifier for cached resources. The URI is expected to * in an absolute form. * * @param requestUri request URI * @return cache key */ public String generateKey(final URI requestUri) { try { final URI normalizeRequestUri = HttpCacheSupport.normalize(requestUri); return normalizeRequestUri.toASCIIString(); } catch (final URISyntaxException ex) { return requestUri.toASCIIString(); } } /** * Computes a key for the given {@link HttpHost} and {@link HttpRequest} * that can be used as a unique identifier for cached resources. * * @param host The host for this request * @param request the {@link HttpRequest} * @return cache key */ public String generateKey(final HttpHost host, final HttpRequest request) { final String s = HttpCacheSupport.getRequestUri(request, host); try { return generateKey(new URI(s)); } catch (final URISyntaxException ex) { return s; } } private String getFullHeaderValue(final Header[] headers) { if (headers == null) { return ""; } final StringBuilder buf = new StringBuilder(); for (int i = 0; i < headers.length; i++) { final Header hdr = headers[i]; if (i > 0) { buf.append(", "); } buf.append(hdr.getValue().trim()); } return buf.toString(); } /** * Computes a key for the given {@link HttpHost} and {@link HttpRequest} * that can be used as a unique identifier for cached resources. if the request has a * {@literal VARY} header the identifier will also include variant key. * * @param host The host for this request * @param request the {@link HttpRequest} * @param entry the parent entry used to track the variants * @return cache key */ public String generateKey(final HttpHost host, final HttpRequest request, final HttpCacheEntry entry) { if (!entry.hasVariants()) { return generateKey(host, request); } return generateVariantKey(request, entry) + generateKey(host, request); } /** * Computes a "variant key" from the headers of a given request that are * covered by the Vary header of a given cache entry. Any request whose * varying headers match those of this request should have the same * variant key. * @param req originating request * @param entry cache entry in question that has variants * @return variant key */ public String generateVariantKey(final HttpRequest req, final HttpCacheEntry entry) { final List variantHeaderNames = new ArrayList<>(); final Iterator it = MessageSupport.iterate(entry, HeaderConstants.VARY); while (it.hasNext()) { final HeaderElement elt = it.next(); variantHeaderNames.add(elt.getName()); } Collections.sort(variantHeaderNames); final StringBuilder buf; try { buf = new StringBuilder("{"); boolean first = true; for (final String headerName : variantHeaderNames) { if (!first) { buf.append("&"); } buf.append(URLEncoder.encode(headerName, StandardCharsets.UTF_8.name())); buf.append("="); buf.append(URLEncoder.encode(getFullHeaderValue(req.getHeaders(headerName)), StandardCharsets.UTF_8.name())); first = false; } buf.append("}"); } catch (final UnsupportedEncodingException uee) { throw new RuntimeException("couldn't encode to UTF-8", uee); } return buf.toString(); } } CacheMap.java000066400000000000000000000033761434266521000361570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.LinkedHashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; final class CacheMap extends LinkedHashMap { private static final long serialVersionUID = -7750025207539768511L; private final int maxEntries; CacheMap(final int maxEntries) { super(20, 0.75f, true); this.maxEntries = maxEntries; } @Override protected boolean removeEldestEntry(final Map.Entry eldest) { return size() > this.maxEntries; } } CacheRevalidatorBase.java000066400000000000000000000155641434266521000405130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.Closeable; import java.io.IOException; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.schedule.ConcurrentCountMap; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Abstract cache re-validation class. */ class CacheRevalidatorBase implements Closeable { interface ScheduledExecutor { Future schedule(Runnable command, TimeValue timeValue) throws RejectedExecutionException; void shutdown(); void awaitTermination(final Timeout timeout) throws InterruptedException; } public static ScheduledExecutor wrap(final ScheduledExecutorService executorService) { return new ScheduledExecutor() { @Override public ScheduledFuture schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException { Args.notNull(command, "Runnable"); Args.notNull(timeValue, "Time value"); return executorService.schedule(command, timeValue.getDuration(), timeValue.getTimeUnit()); } @Override public void shutdown() { executorService.shutdown(); } @Override public void awaitTermination(final Timeout timeout) throws InterruptedException { Args.notNull(timeout, "Timeout"); executorService.awaitTermination(timeout.getDuration(), timeout.getTimeUnit()); } }; } private final ScheduledExecutor scheduledExecutor; private final SchedulingStrategy schedulingStrategy; private final Set pendingRequest; private final ConcurrentCountMap failureCache; private static final Logger LOG = LoggerFactory.getLogger(CacheRevalidatorBase.class); /** * Create CacheValidator which will make ache revalidation requests * using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutor}. */ public CacheRevalidatorBase( final ScheduledExecutor scheduledExecutor, final SchedulingStrategy schedulingStrategy) { this.scheduledExecutor = scheduledExecutor; this.schedulingStrategy = schedulingStrategy; this.pendingRequest = new HashSet<>(); this.failureCache = new ConcurrentCountMap<>(); } /** * Create CacheValidator which will make ache revalidation requests * using the supplied {@link SchedulingStrategy} and {@link ScheduledThreadPoolExecutor}. */ public CacheRevalidatorBase( final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor, final SchedulingStrategy schedulingStrategy) { this(wrap(scheduledThreadPoolExecutor), schedulingStrategy); } /** * Schedules an asynchronous re-validation */ void scheduleRevalidation(final String cacheKey, final Runnable command) { synchronized (pendingRequest) { if (!pendingRequest.contains(cacheKey)) { final int consecutiveFailedAttempts = failureCache.getCount(cacheKey); final TimeValue executionTime = schedulingStrategy.schedule(consecutiveFailedAttempts); try { scheduledExecutor.schedule(command, executionTime); pendingRequest.add(cacheKey); } catch (final RejectedExecutionException ex) { LOG.debug("Revalidation of cache entry with key {} could not be scheduled", cacheKey, ex); } } } } @Override public void close() throws IOException { scheduledExecutor.shutdown(); } public void awaitTermination(final Timeout timeout) throws InterruptedException { Args.notNull(timeout, "Timeout"); scheduledExecutor.awaitTermination(timeout); } void jobSuccessful(final String identifier) { failureCache.resetCount(identifier); synchronized (pendingRequest) { pendingRequest.remove(identifier); } } void jobFailed(final String identifier) { failureCache.increaseCount(identifier); synchronized (pendingRequest) { pendingRequest.remove(identifier); } } Set getScheduledIdentifiers() { synchronized (pendingRequest) { return new HashSet<>(pendingRequest); } } /** * Determines if the given response is generated from a stale cache entry. * @param httpResponse the response to be checked * @return whether the response is stale or not */ boolean isStale(final HttpResponse httpResponse) { for (final Iterator
it = httpResponse.headerIterator(HeaderConstants.WARNING); it.hasNext(); ) { /* * warn-codes * 110 = Response is stale * 111 = Revalidation failed */ final Header warning = it.next(); final String warningValue = warning.getValue(); if (warningValue.startsWith("110") || warningValue.startsWith("111")) { return true; } } return false; } } CacheUpdateHandler.java000066400000000000000000000155541434266521000401630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.HeaderGroup; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; /** * Creates new {@link HttpCacheEntry}s and updates existing ones with new or updated information * based on the response from the origin server. */ class CacheUpdateHandler { private final ResourceFactory resourceFactory; CacheUpdateHandler() { this(new HeapResourceFactory()); } CacheUpdateHandler(final ResourceFactory resourceFactory) { super(); this.resourceFactory = resourceFactory; } /** * Creates a cache entry for the given request, origin response message and response content. */ public HttpCacheEntry createCacheEntry( final HttpRequest request, final HttpResponse originResponse, final ByteArrayBuffer content, final Instant requestSent, final Instant responseReceived) throws ResourceIOException { return new HttpCacheEntry( requestSent, responseReceived, originResponse.getCode(), originResponse.getHeaders(), content != null ? resourceFactory.generate(request.getRequestUri(), content.array(), 0, content.length()) : null); } /** * Update the entry with the new information from the response. Should only be used for * 304 responses. */ public HttpCacheEntry updateCacheEntry( final String requestId, final HttpCacheEntry entry, final Instant requestDate, final Instant responseDate, final HttpResponse response) throws ResourceIOException { Args.check(response.getCode() == HttpStatus.SC_NOT_MODIFIED, "Response must have 304 status code"); final Header[] mergedHeaders = mergeHeaders(entry, response); Resource resource = null; if (entry.getResource() != null) { resource = resourceFactory.copy(requestId, entry.getResource()); } return new HttpCacheEntry( requestDate, responseDate, entry.getStatus(), mergedHeaders, resource); } @SuppressWarnings("deprecation") public HttpCacheEntry updateParentCacheEntry( final String requestId, final HttpCacheEntry existing, final HttpCacheEntry entry, final String variantKey, final String variantCacheKey) throws ResourceIOException { HttpCacheEntry src = existing; if (src == null) { src = entry; } Resource resource = null; if (src.getResource() != null) { resource = resourceFactory.copy(requestId, src.getResource()); } final Map variantMap = new HashMap<>(src.getVariantMap()); variantMap.put(variantKey, variantCacheKey); return new HttpCacheEntry( src.getRequestInstant(), src.getResponseInstant(), src.getStatus(), src.getHeaders(), resource, variantMap); } private Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) { if (DateSupport.isAfter(entry, response, HttpHeaders.DATE)) { return entry.getHeaders(); } final HeaderGroup headerGroup = new HeaderGroup(); headerGroup.setHeaders(entry.getHeaders()); // Remove cache headers that match response for (final Iterator
it = response.headerIterator(); it.hasNext(); ) { final Header responseHeader = it.next(); // Since we do not expect a content in a 304 response, should retain the original Content-Encoding header if (HttpHeaders.CONTENT_ENCODING.equals(responseHeader.getName()) || HttpHeaders.CONTENT_LENGTH.equals(responseHeader.getName())) { continue; } headerGroup.removeHeaders(responseHeader.getName()); } // remove cache entry 1xx warnings for (final Iterator
it = headerGroup.headerIterator(); it.hasNext(); ) { final Header cacheHeader = it.next(); if (HeaderConstants.WARNING.equalsIgnoreCase(cacheHeader.getName())) { final String warningValue = cacheHeader.getValue(); if (warningValue != null && warningValue.startsWith("1")) { it.remove(); } } } for (final Iterator
it = response.headerIterator(); it.hasNext(); ) { final Header responseHeader = it.next(); // Since we do not expect a content in a 304 response, should update the cache entry with Content-Encoding if (HttpHeaders.CONTENT_ENCODING.equals(responseHeader.getName()) || HttpHeaders.CONTENT_LENGTH.equals(responseHeader.getName())) { continue; } headerGroup.addHeader(responseHeader); } return headerGroup.getHeaders(); } } CacheValidityPolicy.java000066400000000000000000000276231434266521000404100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Duration; import java.time.Instant; import java.util.Iterator; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.TimeValue; class CacheValidityPolicy { public static final TimeValue MAX_AGE = TimeValue.ofSeconds(Integer.MAX_VALUE + 1L); CacheValidityPolicy() { super(); } public TimeValue getCurrentAge(final HttpCacheEntry entry, final Instant now) { return TimeValue.ofSeconds(getCorrectedInitialAge(entry).toSeconds() + getResidentTime(entry, now).toSeconds()); } public TimeValue getFreshnessLifetime(final HttpCacheEntry entry) { final long maxAge = getMaxAge(entry); if (maxAge > -1) { return TimeValue.ofSeconds(maxAge); } final Instant dateValue = entry.getInstant(); if (dateValue == null) { return TimeValue.ZERO_MILLISECONDS; } final Instant expiry = DateUtils.parseStandardDate(entry, HeaderConstants.EXPIRES); if (expiry == null) { return TimeValue.ZERO_MILLISECONDS; } final Duration diff = Duration.between(dateValue, expiry); return TimeValue.ofSeconds(diff.getSeconds()); } public boolean isResponseFresh(final HttpCacheEntry entry, final Instant now) { return getCurrentAge(entry, now).compareTo(getFreshnessLifetime(entry)) == -1; } /** * Decides if this response is fresh enough based Last-Modified and Date, if available. * This entry is meant to be used when isResponseFresh returns false. * * The algorithm is as follows: * if last-modified and date are defined, freshness lifetime is coefficient*(date-lastModified), * else freshness lifetime is defaultLifetime * * @param entry the cache entry * @param now what time is it currently (When is right NOW) * @param coefficient Part of the heuristic for cache entry freshness * @param defaultLifetime How long can I assume a cache entry is default TTL * @return {@code true} if the response is fresh */ public boolean isResponseHeuristicallyFresh(final HttpCacheEntry entry, final Instant now, final float coefficient, final TimeValue defaultLifetime) { return getCurrentAge(entry, now).compareTo(getHeuristicFreshnessLifetime(entry, coefficient, defaultLifetime)) == -1; } public TimeValue getHeuristicFreshnessLifetime(final HttpCacheEntry entry, final float coefficient, final TimeValue defaultLifetime) { final Instant dateValue = entry.getInstant(); final Instant lastModifiedValue = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED); if (dateValue != null && lastModifiedValue != null) { final Duration diff = Duration.between(lastModifiedValue, dateValue); if (diff.isNegative()) { return TimeValue.ZERO_MILLISECONDS; } return TimeValue.ofSeconds((long) (coefficient * diff.getSeconds())); } return defaultLifetime; } public boolean isRevalidatable(final HttpCacheEntry entry) { return entry.getFirstHeader(HeaderConstants.ETAG) != null || entry.getFirstHeader(HeaderConstants.LAST_MODIFIED) != null; } public boolean mustRevalidate(final HttpCacheEntry entry) { return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE); } public boolean proxyRevalidate(final HttpCacheEntry entry) { return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE); } public boolean mayReturnStaleWhileRevalidating(final HttpCacheEntry entry, final Instant now) { final Iterator it = MessageSupport.iterate(entry, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.STALE_WHILE_REVALIDATE.equalsIgnoreCase(elt.getName())) { try { // in seconds final int allowedStalenessLifetime = Integer.parseInt(elt.getValue()); if (getStaleness(entry, now).compareTo(TimeValue.ofSeconds(allowedStalenessLifetime)) <= 0) { return true; } } catch (final NumberFormatException nfe) { // skip malformed directive } } } return false; } public boolean mayReturnStaleIfError(final HttpRequest request, final HttpCacheEntry entry, final Instant now) { final TimeValue staleness = getStaleness(entry, now); return mayReturnStaleIfError(request, HeaderConstants.CACHE_CONTROL, staleness) || mayReturnStaleIfError(entry, HeaderConstants.CACHE_CONTROL, staleness); } private boolean mayReturnStaleIfError(final MessageHeaders headers, final String name, final TimeValue staleness) { boolean result = false; final Iterator it = MessageSupport.iterate(headers, name); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.STALE_IF_ERROR.equals(elt.getName())) { try { // in seconds final int staleIfError = Integer.parseInt(elt.getValue()); if (staleness.compareTo(TimeValue.ofSeconds(staleIfError)) <= 0) { result = true; break; } } catch (final NumberFormatException nfe) { // skip malformed directive } } } return result; } /** * This matters for deciding whether the cache entry is valid to serve as a * response. If these values do not match, we might have a partial response * * @param entry The cache entry we are currently working with * @return boolean indicating whether actual length matches Content-Length */ protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) { final Header h = entry.getFirstHeader(HttpHeaders.CONTENT_LENGTH); if (h != null) { try { final long responseLen = Long.parseLong(h.getValue()); final Resource resource = entry.getResource(); if (resource == null) { return false; } final long resourceLen = resource.length(); return responseLen == resourceLen; } catch (final NumberFormatException ex) { return false; } } return true; } protected TimeValue getApparentAge(final HttpCacheEntry entry) { final Instant dateValue = entry.getInstant(); if (dateValue == null) { return MAX_AGE; } final Duration diff = Duration.between(dateValue, entry.getResponseInstant()); if (diff.isNegative()) { return TimeValue.ZERO_MILLISECONDS; } return TimeValue.ofSeconds(diff.getSeconds()); } protected long getAgeValue(final HttpCacheEntry entry) { // This is a header value, we leave as-is long ageValue = 0; for (final Header hdr : entry.getHeaders(HeaderConstants.AGE)) { long hdrAge; try { hdrAge = Long.parseLong(hdr.getValue()); if (hdrAge < 0) { hdrAge = MAX_AGE.toSeconds(); } } catch (final NumberFormatException nfe) { hdrAge = MAX_AGE.toSeconds(); } ageValue = (hdrAge > ageValue) ? hdrAge : ageValue; } return ageValue; } protected TimeValue getCorrectedReceivedAge(final HttpCacheEntry entry) { final TimeValue apparentAge = getApparentAge(entry); final long ageValue = getAgeValue(entry); return (apparentAge.toSeconds() > ageValue) ? apparentAge : TimeValue.ofSeconds(ageValue); } protected TimeValue getResponseDelay(final HttpCacheEntry entry) { final Duration diff = Duration.between(entry.getRequestInstant(), entry.getResponseInstant()); return TimeValue.ofSeconds(diff.getSeconds()); } protected TimeValue getCorrectedInitialAge(final HttpCacheEntry entry) { return TimeValue.ofSeconds(getCorrectedReceivedAge(entry).toSeconds() + getResponseDelay(entry).toSeconds()); } protected TimeValue getResidentTime(final HttpCacheEntry entry, final Instant now) { final Duration diff = Duration.between(entry.getResponseInstant(), now); return TimeValue.ofSeconds(diff.getSeconds()); } protected long getMaxAge(final HttpCacheEntry entry) { // This is a header value, we leave as-is long maxAge = -1; final Iterator it = MessageSupport.iterate(entry, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName()) || "s-maxage".equals(elt.getName())) { try { final long currMaxAge = Long.parseLong(elt.getValue()); if (maxAge == -1 || currMaxAge < maxAge) { maxAge = currMaxAge; } } catch (final NumberFormatException nfe) { // be conservative if can't parse maxAge = 0; } } } return maxAge; } public boolean hasCacheControlDirective(final HttpCacheEntry entry, final String directive) { final Iterator it = MessageSupport.iterate(entry, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (directive.equalsIgnoreCase(elt.getName())) { return true; } } return false; } public TimeValue getStaleness(final HttpCacheEntry entry, final Instant now) { final TimeValue age = getCurrentAge(entry, now); final TimeValue freshness = getFreshnessLifetime(entry); if (age.compareTo(freshness) <= 0) { return TimeValue.ZERO_MILLISECONDS; } return TimeValue.ofSeconds(age.toSeconds() - freshness.toSeconds()); } } CacheableRequestPolicy.java000066400000000000000000000071071434266521000410720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.Iterator; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Determines if an HttpRequest is allowed to be served from the cache. */ class CacheableRequestPolicy { private static final Logger LOG = LoggerFactory.getLogger(CacheableRequestPolicy.class); /** * Determines if an HttpRequest can be served from the cache. * * @param request * an HttpRequest * @return boolean Is it possible to serve this request from cache */ public boolean isServableFromCache(final HttpRequest request) { final String method = request.getMethod(); final ProtocolVersion pv = request.getVersion() != null ? request.getVersion() : HttpVersion.DEFAULT; if (HttpVersion.HTTP_1_1.compareToVersion(pv) != 0) { LOG.debug("non-HTTP/1.1 request is not serveable from cache"); return false; } if (!method.equals(HeaderConstants.GET_METHOD) && !method.equals(HeaderConstants.HEAD_METHOD)) { if (LOG.isDebugEnabled()) { LOG.debug("{} request is not serveable from cache", method); } return false; } if (request.countHeaders(HeaderConstants.PRAGMA) > 0) { LOG.debug("request with Pragma header is not serveable from cache"); return false; } final Iterator it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement cacheControlElement = it.next(); if (HeaderConstants.CACHE_CONTROL_NO_STORE.equalsIgnoreCase(cacheControlElement.getName())) { LOG.debug("Request with no-store is not serveable from cache"); return false; } if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(cacheControlElement.getName())) { LOG.debug("Request with no-cache is not serveable from cache"); return false; } } LOG.debug("Request is serveable from cache"); return true; } } CachedHttpResponseGenerator.java000066400000000000000000000201751434266521000421070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.util.TimeValue; /** * Rebuilds an {@link HttpResponse} from a {@link HttpCacheEntry} */ class CachedHttpResponseGenerator { private final CacheValidityPolicy validityStrategy; CachedHttpResponseGenerator(final CacheValidityPolicy validityStrategy) { super(); this.validityStrategy = validityStrategy; } /** * If it is legal to use cached content in response response to the {@link HttpRequest} then * generate an {@link HttpResponse} based on {@link HttpCacheEntry}. * @param request {@link HttpRequest} to generate the response for * @param entry {@link HttpCacheEntry} to transform into an {@link HttpResponse} * @return {@link SimpleHttpResponse} constructed response */ SimpleHttpResponse generateResponse(final HttpRequest request, final HttpCacheEntry entry) throws ResourceIOException { final Instant now =Instant.now(); final SimpleHttpResponse response = new SimpleHttpResponse(entry.getStatus()); response.setVersion(HttpVersion.DEFAULT); response.setHeaders(entry.getHeaders()); if (responseShouldContainEntity(request, entry)) { final Resource resource = entry.getResource(); final Header h = entry.getFirstHeader(HttpHeaders.CONTENT_TYPE); final ContentType contentType = h != null ? ContentType.parse(h.getValue()) : null; final byte[] content = resource.get(); addMissingContentLengthHeader(response, content); response.setBody(content, contentType); } final TimeValue age = this.validityStrategy.getCurrentAge(entry, now); if (TimeValue.isPositive(age)) { if (age.compareTo(CacheValidityPolicy.MAX_AGE) >= 0) { response.setHeader(HeaderConstants.AGE, "" + CacheValidityPolicy.MAX_AGE.toSeconds()); } else { response.setHeader(HeaderConstants.AGE, "" + age.toSeconds()); } } return response; } /** * Generate a 304 - Not Modified response from the {@link HttpCacheEntry}. This should be * used to respond to conditional requests, when the entry exists or has been re-validated. */ SimpleHttpResponse generateNotModifiedResponse(final HttpCacheEntry entry) { final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); // The response MUST include the following headers // (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) // - Date, unless its omission is required by section 14.8.1 Header dateHeader = entry.getFirstHeader(HttpHeaders.DATE); if (dateHeader == null) { dateHeader = new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now())); } response.addHeader(dateHeader); // - ETag and/or Content-Location, if the header would have been sent // in a 200 response to the same request final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); if (etagHeader != null) { response.addHeader(etagHeader); } final Header contentLocationHeader = entry.getFirstHeader(HttpHeaders.CONTENT_LOCATION); if (contentLocationHeader != null) { response.addHeader(contentLocationHeader); } // - Expires, Cache-Control, and/or Vary, if the field-value might // differ from that sent in any previous response for the same // variant final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES); if (expiresHeader != null) { response.addHeader(expiresHeader); } final Header cacheControlHeader = entry.getFirstHeader(HeaderConstants.CACHE_CONTROL); if (cacheControlHeader != null) { response.addHeader(cacheControlHeader); } final Header varyHeader = entry.getFirstHeader(HeaderConstants.VARY); if (varyHeader != null) { response.addHeader(varyHeader); } return response; } private void addMissingContentLengthHeader(final HttpResponse response, final byte[] body) { if (transferEncodingIsPresent(response)) { return; } // Some well known proxies respond with Content-Length=0, when returning 304. For robustness, always // use the cached entity's content length, as modern browsers do. response.setHeader(HttpHeaders.CONTENT_LENGTH, Integer.toString(body.length)); } private boolean transferEncodingIsPresent(final HttpResponse response) { final Header hdr = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING); return hdr != null; } private boolean responseShouldContainEntity(final HttpRequest request, final HttpCacheEntry cacheEntry) { return request.getMethod().equals(HeaderConstants.GET_METHOD) && cacheEntry.getResource() != null; } /** * Extract error information about the {@link HttpRequest} telling the 'caller' * that a problem occurred. * * @param errorCheck What type of error should I get * @return The {@link HttpResponse} that is the error generated */ public SimpleHttpResponse getErrorForRequest(final RequestProtocolError errorCheck) { switch (errorCheck) { case BODY_BUT_NO_LENGTH_ERROR: return SimpleHttpResponse.create(HttpStatus.SC_LENGTH_REQUIRED); case WEAK_ETAG_AND_RANGE_ERROR: return SimpleHttpResponse.create(HttpStatus.SC_BAD_REQUEST, "Weak eTag not compatible with byte range", ContentType.DEFAULT_TEXT); case WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR: return SimpleHttpResponse.create(HttpStatus.SC_BAD_REQUEST, "Weak eTag not compatible with PUT or DELETE requests"); case NO_CACHE_DIRECTIVE_WITH_FIELD_NAME: return SimpleHttpResponse.create(HttpStatus.SC_BAD_REQUEST, "No-Cache directive MUST NOT include a field name"); default: throw new IllegalStateException( "The request was compliant, therefore no error can be generated for it."); } } } CachedResponseSuitabilityChecker.java000066400000000000000000000355341434266521000431150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.Iterator; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.TimeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Determines whether a given {@link HttpCacheEntry} is suitable to be * used as a response for a given {@link HttpRequest}. */ class CachedResponseSuitabilityChecker { private static final Logger LOG = LoggerFactory.getLogger(CachedResponseSuitabilityChecker.class); private final boolean sharedCache; private final boolean useHeuristicCaching; private final float heuristicCoefficient; private final TimeValue heuristicDefaultLifetime; private final CacheValidityPolicy validityStrategy; CachedResponseSuitabilityChecker(final CacheValidityPolicy validityStrategy, final CacheConfig config) { super(); this.validityStrategy = validityStrategy; this.sharedCache = config.isSharedCache(); this.useHeuristicCaching = config.isHeuristicCachingEnabled(); this.heuristicCoefficient = config.getHeuristicCoefficient(); this.heuristicDefaultLifetime = config.getHeuristicDefaultLifetime(); } CachedResponseSuitabilityChecker(final CacheConfig config) { this(new CacheValidityPolicy(), config); } private boolean isFreshEnough(final HttpCacheEntry entry, final HttpRequest request, final Instant now) { if (validityStrategy.isResponseFresh(entry, now)) { return true; } if (useHeuristicCaching && validityStrategy.isResponseHeuristicallyFresh(entry, now, heuristicCoefficient, heuristicDefaultLifetime)) { return true; } if (originInsistsOnFreshness(entry)) { return false; } final long maxStale = getMaxStale(request); if (maxStale == -1) { return false; } return (maxStale > validityStrategy.getStaleness(entry, now).toSeconds()); } private boolean originInsistsOnFreshness(final HttpCacheEntry entry) { if (validityStrategy.mustRevalidate(entry)) { return true; } if (!sharedCache) { return false; } return validityStrategy.proxyRevalidate(entry) || validityStrategy.hasCacheControlDirective(entry, "s-maxage"); } private long getMaxStale(final HttpRequest request) { // This is a header value, we leave as-is long maxStale = -1; final Iterator it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) { if ((elt.getValue() == null || elt.getValue().trim().isEmpty()) && maxStale == -1) { maxStale = Long.MAX_VALUE; } else { try { long val = Long.parseLong(elt.getValue()); if (val < 0) { val = 0; } if (maxStale == -1 || val < maxStale) { maxStale = val; } } catch (final NumberFormatException nfe) { // err on the side of preserving semantic transparency maxStale = 0; } } } } return maxStale; } /** * Determine if I can utilize a {@link HttpCacheEntry} to respond to the given * {@link HttpRequest} * * @param host * {@link HttpHost} * @param request * {@link HttpRequest} * @param entry * {@link HttpCacheEntry} * @param now * Right now in time * @return boolean yes/no answer */ public boolean canCachedResponseBeUsed(final HttpHost host, final HttpRequest request, final HttpCacheEntry entry, final Instant now) { if (!isFreshEnough(entry, request, now)) { LOG.debug("Cache entry is not fresh enough"); return false; } if (isGet(request) && !validityStrategy.contentLengthHeaderMatchesActualLength(entry)) { LOG.debug("Cache entry Content-Length and header information do not match"); return false; } if (hasUnsupportedConditionalHeaders(request)) { LOG.debug("Request contains unsupported conditional headers"); return false; } if (!isConditional(request) && entry.getStatus() == HttpStatus.SC_NOT_MODIFIED) { LOG.debug("Unconditional request and non-modified cached response"); return false; } if (isConditional(request) && !allConditionalsMatch(request, entry, now)) { LOG.debug("Conditional request and with mismatched conditions"); return false; } if (hasUnsupportedCacheEntryForGet(request, entry)) { LOG.debug("HEAD response caching enabled but the cache entry does not contain a " + "request method, entity or a 204 response"); return false; } final Iterator it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) { LOG.debug("Response contained NO CACHE directive, cache was not suitable"); return false; } if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elt.getName())) { LOG.debug("Response contained NO STORE directive, cache was not suitable"); return false; } if (HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) { try { // in seconds final int maxAge = Integer.parseInt(elt.getValue()); if (validityStrategy.getCurrentAge(entry, now).toSeconds() > maxAge) { LOG.debug("Response from cache was not suitable due to max age"); return false; } } catch (final NumberFormatException ex) { // err conservatively LOG.debug("Response from cache was malformed: {}", ex.getMessage()); return false; } } if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) { try { // in seconds final int maxStale = Integer.parseInt(elt.getValue()); if (validityStrategy.getFreshnessLifetime(entry).toSeconds() > maxStale) { LOG.debug("Response from cache was not suitable due to max stale freshness"); return false; } } catch (final NumberFormatException ex) { // err conservatively LOG.debug("Response from cache was malformed: {}", ex.getMessage()); return false; } } if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())) { try { // in seconds final long minFresh = Long.parseLong(elt.getValue()); if (minFresh < 0L) { return false; } final TimeValue age = validityStrategy.getCurrentAge(entry, now); final TimeValue freshness = validityStrategy.getFreshnessLifetime(entry); if (freshness.toSeconds() - age.toSeconds() < minFresh) { LOG.debug("Response from cache was not suitable due to min fresh " + "freshness requirement"); return false; } } catch (final NumberFormatException ex) { // err conservatively LOG.debug("Response from cache was malformed: {}", ex.getMessage()); return false; } } } LOG.debug("Response from cache was suitable"); return true; } private boolean isGet(final HttpRequest request) { return request.getMethod().equals(HeaderConstants.GET_METHOD); } private boolean entryIsNotA204Response(final HttpCacheEntry entry) { return entry.getStatus() != HttpStatus.SC_NO_CONTENT; } private boolean cacheEntryDoesNotContainMethodAndEntity(final HttpCacheEntry entry) { return entry.getRequestMethod() == null && entry.getResource() == null; } private boolean hasUnsupportedCacheEntryForGet(final HttpRequest request, final HttpCacheEntry entry) { return isGet(request) && cacheEntryDoesNotContainMethodAndEntity(entry) && entryIsNotA204Response(entry); } /** * Is this request the type of conditional request we support? * @param request The current httpRequest being made * @return {@code true} if the request is supported */ public boolean isConditional(final HttpRequest request) { return hasSupportedEtagValidator(request) || hasSupportedLastModifiedValidator(request); } /** * Check that conditionals that are part of this request match * @param request The current httpRequest being made * @param entry the cache entry * @param now right NOW in time * @return {@code true} if the request matches all conditionals */ public boolean allConditionalsMatch(final HttpRequest request, final HttpCacheEntry entry, final Instant now) { final boolean hasEtagValidator = hasSupportedEtagValidator(request); final boolean hasLastModifiedValidator = hasSupportedLastModifiedValidator(request); final boolean etagValidatorMatches = (hasEtagValidator) && etagValidatorMatches(request, entry); final boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry, now); if ((hasEtagValidator && hasLastModifiedValidator) && !(etagValidatorMatches && lastModifiedValidatorMatches)) { return false; } else if (hasEtagValidator && !etagValidatorMatches) { return false; } return !hasLastModifiedValidator || lastModifiedValidatorMatches; } private boolean hasUnsupportedConditionalHeaders(final HttpRequest request) { return (request.getFirstHeader(HeaderConstants.IF_RANGE) != null || request.getFirstHeader(HeaderConstants.IF_MATCH) != null || hasValidDateField(request, HeaderConstants.IF_UNMODIFIED_SINCE)); } private boolean hasSupportedEtagValidator(final HttpRequest request) { return request.containsHeader(HeaderConstants.IF_NONE_MATCH); } private boolean hasSupportedLastModifiedValidator(final HttpRequest request) { return hasValidDateField(request, HeaderConstants.IF_MODIFIED_SINCE); } /** * Check entry against If-None-Match * @param request The current httpRequest being made * @param entry the cache entry * @return boolean does the etag validator match */ private boolean etagValidatorMatches(final HttpRequest request, final HttpCacheEntry entry) { final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); final String etag = (etagHeader != null) ? etagHeader.getValue() : null; final Iterator it = MessageSupport.iterate(request, HeaderConstants.IF_NONE_MATCH); while (it.hasNext()) { final HeaderElement elt = it.next(); final String reqEtag = elt.toString(); if (("*".equals(reqEtag) && etag != null) || reqEtag.equals(etag)) { return true; } } return false; } /** * Check entry against If-Modified-Since, if If-Modified-Since is in the future it is invalid as per * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html * @param request The current httpRequest being made * @param entry the cache entry * @param now right NOW in time * @return boolean Does the last modified header match */ private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Instant now) { final Instant lastModified = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED); if (lastModified == null) { return false; } for (final Header h : request.getHeaders(HeaderConstants.IF_MODIFIED_SINCE)) { final Instant ifModifiedSince = DateUtils.parseStandardDate(h.getValue()); if (ifModifiedSince != null) { if (ifModifiedSince.isAfter(now) || lastModified.isAfter(ifModifiedSince)) { return false; } } } return true; } private boolean hasValidDateField(final HttpRequest request, final String headerName) { for(final Header h : request.getHeaders(headerName)) { final Instant instant = DateUtils.parseStandardDate(h.getValue()); return instant != null; } return false; } } CachingExec.java000066400000000000000000000571441434266521000366610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.io.InputStream; import java.time.Instant; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.methods.SimpleBody; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.cache.CacheResponseStatus; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** *

* Request executor in the request execution chain that is responsible for * transparent client-side caching. *

*

* The current implementation is conditionally * compliant with HTTP/1.1 (meaning all the MUST and MUST NOTs are obeyed), * although quite a lot, though not all, of the SHOULDs and SHOULD NOTs * are obeyed too. *

*

* Folks that would like to experiment with alternative storage backends * should look at the {@link HttpCacheStorage} interface and the related * package documentation there. You may also be interested in the provided * {@link org.apache.hc.client5.http.impl.cache.ehcache.EhcacheHttpCacheStorage * EhCache} and {@link * org.apache.hc.client5.http.impl.cache.memcached.MemcachedHttpCacheStorage * memcached} storage backends. *

*

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 4.3 */ class CachingExec extends CachingExecBase implements ExecChainHandler { private final HttpCache responseCache; private final DefaultCacheRevalidator cacheRevalidator; private final ConditionalRequestBuilder conditionalRequestBuilder; private static final Logger LOG = LoggerFactory.getLogger(CachingExec.class); CachingExec(final HttpCache cache, final DefaultCacheRevalidator cacheRevalidator, final CacheConfig config) { super(config); this.responseCache = Args.notNull(cache, "Response cache"); this.cacheRevalidator = cacheRevalidator; this.conditionalRequestBuilder = new ConditionalRequestBuilder<>(classicHttpRequest -> ClassicRequestBuilder.copy(classicHttpRequest).build()); } CachingExec( final HttpCache responseCache, final CacheValidityPolicy validityPolicy, final ResponseCachingPolicy responseCachingPolicy, final CachedHttpResponseGenerator responseGenerator, final CacheableRequestPolicy cacheableRequestPolicy, final CachedResponseSuitabilityChecker suitabilityChecker, final ResponseProtocolCompliance responseCompliance, final RequestProtocolCompliance requestCompliance, final DefaultCacheRevalidator cacheRevalidator, final ConditionalRequestBuilder conditionalRequestBuilder, final CacheConfig config) { super(validityPolicy, responseCachingPolicy, responseGenerator, cacheableRequestPolicy, suitabilityChecker, responseCompliance, requestCompliance, config); this.responseCache = responseCache; this.cacheRevalidator = cacheRevalidator; this.conditionalRequestBuilder = conditionalRequestBuilder; } CachingExec( final HttpCache cache, final ScheduledExecutorService executorService, final SchedulingStrategy schedulingStrategy, final CacheConfig config) { this(cache, executorService != null ? new DefaultCacheRevalidator(executorService, schedulingStrategy) : null, config); } CachingExec( final ResourceFactory resourceFactory, final HttpCacheStorage storage, final ScheduledExecutorService executorService, final SchedulingStrategy schedulingStrategy, final CacheConfig config) { this(new BasicHttpCache(resourceFactory, storage), executorService, schedulingStrategy, config); } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final HttpRoute route = scope.route; final HttpClientContext context = scope.clientContext; context.setAttribute(HttpClientContext.HTTP_ROUTE, scope.route); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); final URIAuthority authority = request.getAuthority(); final String scheme = request.getScheme(); final HttpHost target = authority != null ? new HttpHost(scheme, authority) : route.getTargetHost(); final String via = generateViaHeader(request); // default response context setResponseStatus(context, CacheResponseStatus.CACHE_MISS); if (clientRequestsOurOptions(request)) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return new BasicClassicHttpResponse(HttpStatus.SC_NOT_IMPLEMENTED); } final SimpleHttpResponse fatalErrorResponse = getFatallyNonCompliantResponse(request, context); if (fatalErrorResponse != null) { return convert(fatalErrorResponse, scope); } requestCompliance.makeRequestCompliant(request); request.addHeader("Via",via); if (!cacheableRequestPolicy.isServableFromCache(request)) { LOG.debug("Request is not servable from cache"); responseCache.flushCacheEntriesInvalidatedByRequest(target, request); return callBackend(target, request, scope, chain); } final HttpCacheEntry entry = responseCache.getCacheEntry(target, request); if (entry == null) { LOG.debug("Cache miss"); return handleCacheMiss(target, request, scope, chain); } else { return handleCacheHit(target, request, scope, chain, entry); } } private static ClassicHttpResponse convert(final SimpleHttpResponse cacheResponse, final ExecChain.Scope scope) { if (cacheResponse == null) { return null; } final ClassicHttpResponse response = new BasicClassicHttpResponse(cacheResponse.getCode(), cacheResponse.getReasonPhrase()); for (final Iterator
it = cacheResponse.headerIterator(); it.hasNext(); ) { response.addHeader(it.next()); } response.setVersion(cacheResponse.getVersion() != null ? cacheResponse.getVersion() : HttpVersion.DEFAULT); final SimpleBody body = cacheResponse.getBody(); if (body != null) { final ContentType contentType = body.getContentType(); final Header h = response.getFirstHeader(HttpHeaders.CONTENT_ENCODING); final String contentEncoding = h != null ? h.getValue() : null; if (body.isText()) { response.setEntity(new StringEntity(body.getBodyText(), contentType, contentEncoding, false)); } else { response.setEntity(new ByteArrayEntity(body.getBodyBytes(), contentType, contentEncoding, false)); } } scope.clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); return response; } ClassicHttpResponse callBackend( final HttpHost target, final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { final Instant requestDate = getCurrentDate(); LOG.debug("Calling the backend"); final ClassicHttpResponse backendResponse = chain.proceed(request, scope); try { backendResponse.addHeader("Via", generateViaHeader(backendResponse)); return handleBackendResponse(target, request, scope, requestDate, getCurrentDate(), backendResponse); } catch (final IOException | RuntimeException ex) { backendResponse.close(); throw ex; } } private ClassicHttpResponse handleCacheHit( final HttpHost target, final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain, final HttpCacheEntry entry) throws IOException, HttpException { final HttpClientContext context = scope.clientContext; context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); recordCacheHit(target, request); final Instant now = getCurrentDate(); if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) { LOG.debug("Cache hit"); try { return convert(generateCachedResponse(request, context, entry, now), scope); } catch (final ResourceIOException ex) { recordCacheFailure(target, request); if (!mayCallBackend(request)) { return convert(generateGatewayTimeout(context), scope); } setResponseStatus(scope.clientContext, CacheResponseStatus.FAILURE); return chain.proceed(request, scope); } } else if (!mayCallBackend(request)) { LOG.debug("Cache entry not suitable but only-if-cached requested"); return convert(generateGatewayTimeout(context), scope); } else if (!(entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request))) { LOG.debug("Revalidating cache entry"); try { if (cacheRevalidator != null && !staleResponseNotAllowed(request, entry, now) && validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) { LOG.debug("Serving stale with asynchronous revalidation"); final String exchangeId = ExecSupport.getNextExchangeId(); context.setExchangeId(exchangeId); final ExecChain.Scope fork = new ExecChain.Scope( exchangeId, scope.route, scope.originalRequest, scope.execRuntime.fork(null), HttpClientContext.create()); final SimpleHttpResponse response = generateCachedResponse(request, context, entry, now); cacheRevalidator.revalidateCacheEntry( responseCache.generateKey(target, request, entry), () -> revalidateCacheEntry(target, request, fork, chain, entry)); return convert(response, scope); } return revalidateCacheEntry(target, request, scope, chain, entry); } catch (final IOException ioex) { return convert(handleRevalidationFailure(request, context, entry, now), scope); } } else { LOG.debug("Cache entry not usable; calling backend"); return callBackend(target, request, scope, chain); } } ClassicHttpResponse revalidateCacheEntry( final HttpHost target, final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain, final HttpCacheEntry cacheEntry) throws IOException, HttpException { Instant requestDate = getCurrentDate(); final ClassicHttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequest( scope.originalRequest, cacheEntry); ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope); try { Instant responseDate = getCurrentDate(); if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) { backendResponse.close(); final ClassicHttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest( scope.originalRequest); requestDate = getCurrentDate(); backendResponse = chain.proceed(unconditional, scope); responseDate = getCurrentDate(); } backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse)); final int statusCode = backendResponse.getCode(); if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) { recordCacheUpdate(scope.clientContext); } if (statusCode == HttpStatus.SC_NOT_MODIFIED) { final HttpCacheEntry updatedEntry = responseCache.updateCacheEntry( target, request, cacheEntry, backendResponse, requestDate, responseDate); if (suitabilityChecker.isConditional(request) && suitabilityChecker.allConditionalsMatch(request, updatedEntry, Instant.now())) { return convert(responseGenerator.generateNotModifiedResponse(updatedEntry), scope); } return convert(responseGenerator.generateResponse(request, updatedEntry), scope); } if (staleIfErrorAppliesTo(statusCode) && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate()) && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) { try { final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, cacheEntry); cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\""); return convert(cachedResponse, scope); } finally { backendResponse.close(); } } return handleBackendResponse(target, conditionalRequest, scope, requestDate, responseDate, backendResponse); } catch (final IOException | RuntimeException ex) { backendResponse.close(); throw ex; } } ClassicHttpResponse handleBackendResponse( final HttpHost target, final ClassicHttpRequest request, final ExecChain.Scope scope, final Instant requestDate, final Instant responseDate, final ClassicHttpResponse backendResponse) throws IOException { responseCompliance.ensureProtocolCompliance(scope.originalRequest, request, backendResponse); responseCache.flushCacheEntriesInvalidatedByExchange(target, request, backendResponse); final boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse); if (cacheable) { storeRequestIfModifiedSinceFor304Response(request, backendResponse); return cacheAndReturnResponse(target, request, backendResponse, scope, requestDate, responseDate); } LOG.debug("Backend response is not cacheable"); responseCache.flushCacheEntriesFor(target, request); return backendResponse; } ClassicHttpResponse cacheAndReturnResponse( final HttpHost target, final HttpRequest request, final ClassicHttpResponse backendResponse, final ExecChain.Scope scope, final Instant requestSent, final Instant responseReceived) throws IOException { LOG.debug("Caching backend response"); final ByteArrayBuffer buf; final HttpEntity entity = backendResponse.getEntity(); if (entity != null) { buf = new ByteArrayBuffer(1024); final InputStream inStream = entity.getContent(); final byte[] tmp = new byte[2048]; long total = 0; int l; while ((l = inStream.read(tmp)) != -1) { buf.append(tmp, 0, l); total += l; if (total > cacheConfig.getMaxObjectSize()) { LOG.debug("Backend response content length exceeds maximum"); backendResponse.setEntity(new CombinedEntity(entity, buf)); return backendResponse; } } } else { buf = null; } backendResponse.close(); final HttpCacheEntry cacheEntry; if (cacheConfig.isFreshnessCheckEnabled()) { final HttpCacheEntry existingEntry = responseCache.getCacheEntry(target, request); if (DateSupport.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) { LOG.debug("Backend already contains fresher cache entry"); cacheEntry = existingEntry; } else { cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived); LOG.debug("Backend response successfully cached"); } } else { cacheEntry = responseCache.createCacheEntry(target, request, backendResponse, buf, requestSent, responseReceived); LOG.debug("Backend response successfully cached (freshness check skipped)"); } return convert(responseGenerator.generateResponse(request, cacheEntry), scope); } private ClassicHttpResponse handleCacheMiss( final HttpHost target, final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { recordCacheMiss(target, request); if (!mayCallBackend(request)) { return new BasicClassicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"); } final Map variants = responseCache.getVariantCacheEntriesWithEtags(target, request); if (variants != null && !variants.isEmpty()) { return negotiateResponseFromVariants(target, request, scope, chain, variants); } return callBackend(target, request, scope, chain); } ClassicHttpResponse negotiateResponseFromVariants( final HttpHost target, final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain, final Map variants) throws IOException, HttpException { final ClassicHttpRequest conditionalRequest = conditionalRequestBuilder.buildConditionalRequestFromVariants(request, variants); final Instant requestDate = getCurrentDate(); final ClassicHttpResponse backendResponse = chain.proceed(conditionalRequest, scope); try { final Instant responseDate = getCurrentDate(); backendResponse.addHeader("Via", generateViaHeader(backendResponse)); if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) { return handleBackendResponse(target, request, scope, requestDate, responseDate, backendResponse); } final Header resultEtagHeader = backendResponse.getFirstHeader(HeaderConstants.ETAG); if (resultEtagHeader == null) { LOG.warn("304 response did not contain ETag"); EntityUtils.consume(backendResponse.getEntity()); backendResponse.close(); return callBackend(target, request, scope, chain); } final String resultEtag = resultEtagHeader.getValue(); final Variant matchingVariant = variants.get(resultEtag); if (matchingVariant == null) { LOG.debug("304 response did not contain ETag matching one sent in If-None-Match"); EntityUtils.consume(backendResponse.getEntity()); backendResponse.close(); return callBackend(target, request, scope, chain); } if (revalidationResponseIsTooOld(backendResponse, matchingVariant.getEntry()) && (request.getEntity() == null || request.getEntity().isRepeatable())) { EntityUtils.consume(backendResponse.getEntity()); backendResponse.close(); final ClassicHttpRequest unconditional = conditionalRequestBuilder.buildUnconditionalRequest(request); return callBackend(target, unconditional, scope, chain); } recordCacheUpdate(scope.clientContext); final HttpCacheEntry responseEntry = responseCache.updateVariantCacheEntry( target, conditionalRequest, backendResponse, matchingVariant, requestDate, responseDate); backendResponse.close(); if (shouldSendNotModifiedResponse(request, responseEntry)) { return convert(responseGenerator.generateNotModifiedResponse(responseEntry), scope); } final SimpleHttpResponse response = responseGenerator.generateResponse(request, responseEntry); responseCache.reuseVariantEntryFor(target, request, matchingVariant); return convert(response, scope); } catch (final IOException | RuntimeException ex) { backendResponse.close(); throw ex; } } } CachingExecBase.java000066400000000000000000000365721434266521000374560ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.time.Instant; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.cache.CacheResponseStatus; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheContext; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.VersionInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CachingExecBase { final static boolean SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS = false; final AtomicLong cacheHits = new AtomicLong(); final AtomicLong cacheMisses = new AtomicLong(); final AtomicLong cacheUpdates = new AtomicLong(); final Map viaHeaders = new ConcurrentHashMap<>(4); final ResponseCachingPolicy responseCachingPolicy; final CacheValidityPolicy validityPolicy; final CachedHttpResponseGenerator responseGenerator; final CacheableRequestPolicy cacheableRequestPolicy; final CachedResponseSuitabilityChecker suitabilityChecker; final ResponseProtocolCompliance responseCompliance; final RequestProtocolCompliance requestCompliance; final CacheConfig cacheConfig; private static final Logger LOG = LoggerFactory.getLogger(CachingExecBase.class); CachingExecBase( final CacheValidityPolicy validityPolicy, final ResponseCachingPolicy responseCachingPolicy, final CachedHttpResponseGenerator responseGenerator, final CacheableRequestPolicy cacheableRequestPolicy, final CachedResponseSuitabilityChecker suitabilityChecker, final ResponseProtocolCompliance responseCompliance, final RequestProtocolCompliance requestCompliance, final CacheConfig config) { this.responseCachingPolicy = responseCachingPolicy; this.validityPolicy = validityPolicy; this.responseGenerator = responseGenerator; this.cacheableRequestPolicy = cacheableRequestPolicy; this.suitabilityChecker = suitabilityChecker; this.requestCompliance = requestCompliance; this.responseCompliance = responseCompliance; this.cacheConfig = config != null ? config : CacheConfig.DEFAULT; } CachingExecBase(final CacheConfig config) { super(); this.cacheConfig = config != null ? config : CacheConfig.DEFAULT; this.validityPolicy = new CacheValidityPolicy(); this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy); this.cacheableRequestPolicy = new CacheableRequestPolicy(); this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, this.cacheConfig); this.responseCompliance = new ResponseProtocolCompliance(); this.requestCompliance = new RequestProtocolCompliance(this.cacheConfig.isWeakETagOnPutDeleteAllowed()); this.responseCachingPolicy = new ResponseCachingPolicy( this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(), this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery(), this.cacheConfig.is303CachingEnabled()); } /** * 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 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 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 cacheUpdates.get(); } /** * @since 5.2 */ SimpleHttpResponse getFatallyNonCompliantResponse( final HttpRequest request, final HttpContext context) { final List fatalError = requestCompliance.requestIsFatallyNonCompliant(request); if (fatalError != null && !fatalError.isEmpty()) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return responseGenerator.getErrorForRequest(fatalError.get(0)); } return null; } void recordCacheMiss(final HttpHost target, final HttpRequest request) { cacheMisses.getAndIncrement(); if (LOG.isDebugEnabled()) { LOG.debug("Cache miss [host: {}; uri: {}]", target, request.getRequestUri()); } } void recordCacheHit(final HttpHost target, final HttpRequest request) { cacheHits.getAndIncrement(); if (LOG.isDebugEnabled()) { LOG.debug("Cache hit [host: {}; uri: {}]", target, request.getRequestUri()); } } void recordCacheFailure(final HttpHost target, final HttpRequest request) { cacheMisses.getAndIncrement(); if (LOG.isDebugEnabled()) { LOG.debug("Cache failure [host: {}; uri: {}]", target, request.getRequestUri()); } } void recordCacheUpdate(final HttpContext context) { cacheUpdates.getAndIncrement(); setResponseStatus(context, CacheResponseStatus.VALIDATED); } SimpleHttpResponse generateCachedResponse( final HttpRequest request, final HttpContext context, final HttpCacheEntry entry, final Instant now) throws ResourceIOException { final SimpleHttpResponse cachedResponse; if (request.containsHeader(HeaderConstants.IF_NONE_MATCH) || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) { cachedResponse = responseGenerator.generateNotModifiedResponse(entry); } else { cachedResponse = responseGenerator.generateResponse(request, entry); } setResponseStatus(context, CacheResponseStatus.CACHE_HIT); if (TimeValue.isPositive(validityPolicy.getStaleness(entry, now))) { cachedResponse.addHeader(HeaderConstants.WARNING,"110 localhost \"Response is stale\""); } return cachedResponse; } SimpleHttpResponse handleRevalidationFailure( final HttpRequest request, final HttpContext context, final HttpCacheEntry entry, final Instant now) throws IOException { if (staleResponseNotAllowed(request, entry, now)) { return generateGatewayTimeout(context); } else { return unvalidatedCacheHit(request, context, entry); } } SimpleHttpResponse generateGatewayTimeout( final HttpContext context) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return SimpleHttpResponse.create(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"); } SimpleHttpResponse unvalidatedCacheHit( final HttpRequest request, final HttpContext context, final HttpCacheEntry entry) throws IOException { final SimpleHttpResponse cachedResponse = responseGenerator.generateResponse(request, entry); setResponseStatus(context, CacheResponseStatus.CACHE_HIT); cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\""); return cachedResponse; } boolean staleResponseNotAllowed(final HttpRequest request, final HttpCacheEntry entry, final Instant now) { return validityPolicy.mustRevalidate(entry) || (cacheConfig.isSharedCache() && validityPolicy.proxyRevalidate(entry)) || explicitFreshnessRequest(request, entry, now); } boolean mayCallBackend(final HttpRequest request) { final Iterator it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("only-if-cached".equals(elt.getName())) { LOG.debug("Request marked only-if-cached"); return false; } } return true; } boolean explicitFreshnessRequest(final HttpRequest request, final HttpCacheEntry entry, final Instant now) { final Iterator it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) { try { // in seconds final int maxStale = Integer.parseInt(elt.getValue()); final TimeValue age = validityPolicy.getCurrentAge(entry, now); final TimeValue lifetime = validityPolicy.getFreshnessLifetime(entry); if (age.toSeconds() - lifetime.toSeconds() > 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; } String generateViaHeader(final HttpMessage msg) { if (msg.getVersion() == null) { msg.setVersion(HttpVersion.DEFAULT); } final ProtocolVersion pv = msg.getVersion(); final String existingEntry = viaHeaders.get(msg.getVersion()); if (existingEntry != null) { return existingEntry; } final VersionInfo vi = VersionInfo.loadVersionInfo("org.apache.hc.client5", getClass().getClassLoader()); final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; final String value; final int major = pv.getMajor(); final int minor = pv.getMinor(); if (URIScheme.HTTP.same(pv.getProtocol())) { value = String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", major, minor, release); } else { value = String.format("%s/%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getProtocol(), major, minor, release); } viaHeaders.put(pv, value); return value; } void setResponseStatus(final HttpContext context, final CacheResponseStatus value) { if (context != null) { context.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 */ boolean supportsRangeAndContentRangeHeaders() { return SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS; } Instant getCurrentDate() { return Instant.now(); } boolean clientRequestsOurOptions(final HttpRequest request) { if (!HeaderConstants.OPTIONS_METHOD.equals(request.getMethod())) { return false; } if (!"*".equals(request.getRequestUri())) { return false; } final Header h = request.getFirstHeader(HeaderConstants.MAX_FORWARDS); return "0".equals(h != null ? h.getValue() : null); } boolean revalidationResponseIsTooOld(final HttpResponse backendResponse, final HttpCacheEntry cacheEntry) { // either backend response or cached entry did not have a valid // Date header, so we can't tell if they are out of order // according to the origin clock; thus we can skip the // unconditional retry recommended in 13.2.6 of RFC 2616. return DateSupport.isBefore(backendResponse, cacheEntry, HttpHeaders.DATE); } boolean shouldSendNotModifiedResponse(final HttpRequest request, final HttpCacheEntry responseEntry) { return (suitabilityChecker.isConditional(request) && suitabilityChecker.allConditionalsMatch(request, responseEntry, Instant.now())); } 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; } /** * 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. */ void storeRequestIfModifiedSinceFor304Response(final HttpRequest request, final HttpResponse backendResponse) { if (backendResponse.getCode() == HttpStatus.SC_NOT_MODIFIED) { final Header h = request.getFirstHeader(HttpHeaders.IF_MODIFIED_SINCE); if (h != null) { backendResponse.addHeader(HttpHeaders.LAST_MODIFIED, h.getValue()); } } } } CachingH2AsyncClientBuilder.java000066400000000000000000000150761434266521000417100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorageAdaptor; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder; import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.http.config.NamedElementChain; /** * Builder for HTTP/2 {@link org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient} * instances capable of client-side caching. * * @since 5.0 */ @Experimental public class CachingH2AsyncClientBuilder extends H2AsyncClientBuilder { private ResourceFactory resourceFactory; private HttpAsyncCacheStorage storage; private File cacheDir; private SchedulingStrategy schedulingStrategy; private CacheConfig cacheConfig; private HttpAsyncCacheInvalidator httpCacheInvalidator; private boolean deleteCache; public static CachingH2AsyncClientBuilder create() { return new CachingH2AsyncClientBuilder(); } protected CachingH2AsyncClientBuilder() { super(); this.deleteCache = true; } public final CachingH2AsyncClientBuilder setResourceFactory(final ResourceFactory resourceFactory) { this.resourceFactory = resourceFactory; return this; } public final CachingH2AsyncClientBuilder setHttpCacheStorage(final HttpCacheStorage storage) { this.storage = storage != null ? new HttpAsyncCacheStorageAdaptor(storage) : null; return this; } public final CachingH2AsyncClientBuilder setHttpCacheStorage(final HttpAsyncCacheStorage storage) { this.storage = storage; return this; } public final CachingH2AsyncClientBuilder setCacheDir(final File cacheDir) { this.cacheDir = cacheDir; return this; } public final CachingH2AsyncClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) { this.schedulingStrategy = schedulingStrategy; return this; } public final CachingH2AsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) { this.cacheConfig = cacheConfig; return this; } public final CachingH2AsyncClientBuilder setHttpCacheInvalidator(final HttpAsyncCacheInvalidator cacheInvalidator) { this.httpCacheInvalidator = cacheInvalidator; return this; } public CachingH2AsyncClientBuilder setDeleteCache(final boolean deleteCache) { this.deleteCache = deleteCache; return this; } @Override protected void customizeExecChain(final NamedElementChain execChainDefinition) { final CacheConfig config = this.cacheConfig != null ? this.cacheConfig : CacheConfig.DEFAULT; // We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version ResourceFactory resourceFactoryCopy = this.resourceFactory; if (resourceFactoryCopy == null) { if (this.cacheDir == null) { resourceFactoryCopy = new HeapResourceFactory(); } else { resourceFactoryCopy = new FileResourceFactory(cacheDir); } } HttpAsyncCacheStorage storageCopy = this.storage; if (storageCopy == null) { if (this.cacheDir == null) { storageCopy = new HttpAsyncCacheStorageAdaptor(new BasicHttpCacheStorage(config)); } else { final ManagedHttpCacheStorage managedStorage = new ManagedHttpCacheStorage(config); if (this.deleteCache) { addCloseable(managedStorage::shutdown); } else { addCloseable(managedStorage); } storageCopy = new HttpAsyncCacheStorageAdaptor(managedStorage); } } final HttpAsyncCache httpCache = new BasicHttpAsyncCache( resourceFactoryCopy, storageCopy, CacheKeyGenerator.INSTANCE, this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator()); DefaultAsyncCacheRevalidator cacheRevalidator = null; if (config.getAsynchronousWorkers() > 0) { final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers()); addCloseable(executorService::shutdownNow); cacheRevalidator = new DefaultAsyncCacheRevalidator( executorService, this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE); } final AsyncCachingExec cachingExec = new AsyncCachingExec( httpCache, cacheRevalidator, config); execChainDefinition.addBefore(ChainElement.PROTOCOL.name(), cachingExec, ChainElement.CACHING.name()); } } CachingHttpAsyncClientBuilder.java000066400000000000000000000151231434266521000423470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorageAdaptor; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.http.config.NamedElementChain; /** * Builder for {@link org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient} * instances capable of client-side caching. * * @since 5.0 */ @Experimental public class CachingHttpAsyncClientBuilder extends HttpAsyncClientBuilder { private ResourceFactory resourceFactory; private HttpAsyncCacheStorage storage; private File cacheDir; private SchedulingStrategy schedulingStrategy; private CacheConfig cacheConfig; private HttpAsyncCacheInvalidator httpCacheInvalidator; private boolean deleteCache; public static CachingHttpAsyncClientBuilder create() { return new CachingHttpAsyncClientBuilder(); } protected CachingHttpAsyncClientBuilder() { super(); this.deleteCache = true; } public final CachingHttpAsyncClientBuilder setResourceFactory(final ResourceFactory resourceFactory) { this.resourceFactory = resourceFactory; return this; } public final CachingHttpAsyncClientBuilder setHttpCacheStorage(final HttpCacheStorage storage) { this.storage = storage != null ? new HttpAsyncCacheStorageAdaptor(storage) : null; return this; } public final CachingHttpAsyncClientBuilder setHttpCacheStorage(final HttpAsyncCacheStorage storage) { this.storage = storage; return this; } public final CachingHttpAsyncClientBuilder setCacheDir(final File cacheDir) { this.cacheDir = cacheDir; return this; } public final CachingHttpAsyncClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) { this.schedulingStrategy = schedulingStrategy; return this; } public final CachingHttpAsyncClientBuilder setCacheConfig(final CacheConfig cacheConfig) { this.cacheConfig = cacheConfig; return this; } public final CachingHttpAsyncClientBuilder setHttpCacheInvalidator(final HttpAsyncCacheInvalidator cacheInvalidator) { this.httpCacheInvalidator = cacheInvalidator; return this; } public CachingHttpAsyncClientBuilder setDeleteCache(final boolean deleteCache) { this.deleteCache = deleteCache; return this; } @Override protected void customizeExecChain(final NamedElementChain execChainDefinition) { final CacheConfig config = this.cacheConfig != null ? this.cacheConfig : CacheConfig.DEFAULT; // We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version ResourceFactory resourceFactoryCopy = this.resourceFactory; if (resourceFactoryCopy == null) { if (this.cacheDir == null) { resourceFactoryCopy = new HeapResourceFactory(); } else { resourceFactoryCopy = new FileResourceFactory(cacheDir); } } HttpAsyncCacheStorage storageCopy = this.storage; if (storageCopy == null) { if (this.cacheDir == null) { storageCopy = new HttpAsyncCacheStorageAdaptor(new BasicHttpCacheStorage(config)); } else { final ManagedHttpCacheStorage managedStorage = new ManagedHttpCacheStorage(config); if (this.deleteCache) { addCloseable(managedStorage::shutdown); } else { addCloseable(managedStorage); } storageCopy = new HttpAsyncCacheStorageAdaptor(managedStorage); } } final HttpAsyncCache httpCache = new BasicHttpAsyncCache( resourceFactoryCopy, storageCopy, CacheKeyGenerator.INSTANCE, this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultAsyncCacheInvalidator()); DefaultAsyncCacheRevalidator cacheRevalidator = null; if (config.getAsynchronousWorkers() > 0) { final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers()); addCloseable(executorService::shutdownNow); cacheRevalidator = new DefaultAsyncCacheRevalidator( executorService, this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE); } final AsyncCachingExec cachingExec = new AsyncCachingExec( httpCache, cacheRevalidator, config); execChainDefinition.addBefore(ChainElement.PROTOCOL.name(), cachingExec, ChainElement.CACHING.name()); } } CachingHttpAsyncClients.java000066400000000000000000000063571434266521000412340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; /** * Factory methods for {@link CloseableHttpAsyncClient} instances * capable of client-side caching. * * @since 5.0 */ public final class CachingHttpAsyncClients { private CachingHttpAsyncClients() { super(); } /** * Creates builder object for construction of custom * {@link CloseableHttpAsyncClient} instances. */ public static CachingHttpAsyncClientBuilder custom() { return CachingHttpAsyncClientBuilder.create(); } /** * Creates {@link CloseableHttpAsyncClient} instance that uses a memory bound * response cache. */ public static CloseableHttpAsyncClient createMemoryBound() { return CachingHttpAsyncClientBuilder.create().build(); } /** * Creates {@link CloseableHttpAsyncClient} instance that uses a file system * bound response cache. * * @param cacheDir location of response cache. */ public static CloseableHttpAsyncClient createFileBound(final File cacheDir) { return CachingHttpAsyncClientBuilder.create().setCacheDir(cacheDir).build(); } /** * Creates builder object for construction of custom HTTP/2 * {@link CloseableHttpAsyncClient} instances. */ public static CachingH2AsyncClientBuilder customHttp2() { return CachingH2AsyncClientBuilder.create(); } /** * Creates HTTP/2 {@link CloseableHttpAsyncClient} instance that uses a memory bound * response cache. */ public static CloseableHttpAsyncClient createHttp2MemoryBound() { return CachingH2AsyncClientBuilder.create().build(); } /** * Creates HTTP/2 {@link CloseableHttpAsyncClient} instance that uses a file system * bound response cache. * * @param cacheDir location of response cache. */ public static CloseableHttpAsyncClient createHttp2FileBound(final File cacheDir) { return CachingH2AsyncClientBuilder.create().setCacheDir(cacheDir).build(); } } CachingHttpClientBuilder.java000066400000000000000000000137631434266521000413610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.hc.client5.http.cache.HttpCacheInvalidator; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.http.config.NamedElementChain; /** * Builder for {@link org.apache.hc.client5.http.impl.classic.CloseableHttpClient} * instances capable of client-side caching. * * @since 4.3 */ public class CachingHttpClientBuilder extends HttpClientBuilder { private ResourceFactory resourceFactory; private HttpCacheStorage storage; private File cacheDir; private SchedulingStrategy schedulingStrategy; private CacheConfig cacheConfig; private HttpCacheInvalidator httpCacheInvalidator; private boolean deleteCache; public static CachingHttpClientBuilder create() { return new CachingHttpClientBuilder(); } protected CachingHttpClientBuilder() { super(); this.deleteCache = true; } public final CachingHttpClientBuilder setResourceFactory( final ResourceFactory resourceFactory) { this.resourceFactory = resourceFactory; return this; } public final CachingHttpClientBuilder setHttpCacheStorage(final HttpCacheStorage storage) { this.storage = storage; return this; } public final CachingHttpClientBuilder setCacheDir(final File cacheDir) { this.cacheDir = cacheDir; return this; } public final CachingHttpClientBuilder setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) { this.schedulingStrategy = schedulingStrategy; return this; } public final CachingHttpClientBuilder setCacheConfig(final CacheConfig cacheConfig) { this.cacheConfig = cacheConfig; return this; } public final CachingHttpClientBuilder setHttpCacheInvalidator(final HttpCacheInvalidator cacheInvalidator) { this.httpCacheInvalidator = cacheInvalidator; return this; } public final CachingHttpClientBuilder setDeleteCache(final boolean deleteCache) { this.deleteCache = deleteCache; return this; } @Override protected void customizeExecChain(final NamedElementChain execChainDefinition) { final CacheConfig config = this.cacheConfig != null ? this.cacheConfig : CacheConfig.DEFAULT; // We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version ResourceFactory resourceFactoryCopy = this.resourceFactory; if (resourceFactoryCopy == null) { if (this.cacheDir == null) { resourceFactoryCopy = new HeapResourceFactory(); } else { resourceFactoryCopy = new FileResourceFactory(cacheDir); } } HttpCacheStorage storageCopy = this.storage; if (storageCopy == null) { if (this.cacheDir == null) { storageCopy = new BasicHttpCacheStorage(config); } else { final ManagedHttpCacheStorage managedStorage = new ManagedHttpCacheStorage(config); if (this.deleteCache) { addCloseable(managedStorage::shutdown); } else { addCloseable(managedStorage); } storageCopy = managedStorage; } } final HttpCache httpCache = new BasicHttpCache( resourceFactoryCopy, storageCopy, CacheKeyGenerator.INSTANCE, this.httpCacheInvalidator != null ? this.httpCacheInvalidator : new DefaultCacheInvalidator()); DefaultCacheRevalidator cacheRevalidator = null; if (config.getAsynchronousWorkers() > 0) { final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(config.getAsynchronousWorkers()); addCloseable(executorService::shutdownNow); cacheRevalidator = new DefaultCacheRevalidator( executorService, this.schedulingStrategy != null ? this.schedulingStrategy : ImmediateSchedulingStrategy.INSTANCE); } final CachingExec cachingExec = new CachingExec( httpCache, cacheRevalidator, config); execChainDefinition.addBefore(ChainElement.PROTOCOL.name(), cachingExec, ChainElement.CACHING.name()); } } CachingHttpClients.java000066400000000000000000000044711434266521000402310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; /** * Factory methods for {@link CloseableHttpClient} instances * capable of client-side caching. * * @since 4.3 */ public final class CachingHttpClients { private CachingHttpClients() { super(); } /** * Creates builder object for construction of custom * {@link CloseableHttpClient} instances. */ public static CachingHttpClientBuilder custom() { return CachingHttpClientBuilder.create(); } /** * Creates {@link CloseableHttpClient} instance that uses a memory bound * response cache. */ public static CloseableHttpClient createMemoryBound() { return CachingHttpClientBuilder.create().build(); } /** * Creates {@link CloseableHttpClient} instance that uses a file system * bound response cache. * * @param cacheDir location of response cache. */ public static CloseableHttpClient createFileBound(final File cacheDir) { return CachingHttpClientBuilder.create().setCacheDir(cacheDir).build(); } } CombinedEntity.java000066400000000000000000000067101434266521000374260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.SequenceInputStream; import java.util.List; import java.util.Set; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; class CombinedEntity implements HttpEntity { private final HttpEntity entity; private final InputStream combinedStream; CombinedEntity(final HttpEntity entity, final ByteArrayBuffer buf) throws IOException { super(); this.entity = entity; this.combinedStream = new SequenceInputStream( new ByteArrayInputStream(buf.array(), 0, buf.length()), entity.getContent()); } @Override public long getContentLength() { return -1; } @Override public String getContentType() { return entity.getContentType(); } @Override public String getContentEncoding() { return entity.getContentEncoding(); } @Override public boolean isChunked() { return true; } @Override public boolean isRepeatable() { return false; } @Override public boolean isStreaming() { return true; } @Override public InputStream getContent() throws IOException, IllegalStateException { return this.combinedStream; } @Override public Set getTrailerNames() { return entity.getTrailerNames(); } @Override public Supplier> getTrailers() { return entity.getTrailers(); } @Override public void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); try (InputStream inStream = getContent()) { int l; final byte[] tmp = new byte[2048]; while ((l = inStream.read(tmp)) != -1) { outStream.write(tmp, 0, l); } } } @Override public void close() throws IOException { try { combinedStream.close(); } finally { entity.close(); } } } ConditionalRequestBuilder.java000066400000000000000000000135321434266521000416340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.Iterator; import java.util.Map; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.core5.function.Factory; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.MessageSupport; class ConditionalRequestBuilder { private final Factory messageCopier; ConditionalRequestBuilder(final Factory messageCopier) { this.messageCopier = messageCopier; } /** * When a {@link HttpCacheEntry} is stale but 'might' be used as a response * to an {@link org.apache.hc.core5.http.HttpRequest} we will attempt to revalidate * the entry with the origin. Build the origin {@link org.apache.hc.core5.http.HttpRequest} * here and return it. * * @param request the original request from the caller * @param cacheEntry the entry that needs to be re-validated * @return the wrapped request */ public T buildConditionalRequest(final T request, final HttpCacheEntry cacheEntry) { final T newRequest = messageCopier.create(request); final Header eTag = cacheEntry.getFirstHeader(HeaderConstants.ETAG); if (eTag != null) { newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, eTag.getValue()); } final Header lastModified = cacheEntry.getFirstHeader(HeaderConstants.LAST_MODIFIED); if (lastModified != null) { newRequest.setHeader(HeaderConstants.IF_MODIFIED_SINCE, lastModified.getValue()); } boolean mustRevalidate = false; final Iterator it = MessageSupport.iterate(cacheEntry, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE.equalsIgnoreCase(elt.getName()) || HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE.equalsIgnoreCase(elt.getName())) { mustRevalidate = true; break; } } if (mustRevalidate) { newRequest.addHeader(HeaderConstants.CACHE_CONTROL, HeaderConstants.CACHE_CONTROL_MAX_AGE + "=0"); } return newRequest; } /** * When a {@link HttpCacheEntry} does not exist for a specific * {@link org.apache.hc.core5.http.HttpRequest} we attempt to see if an existing * {@link HttpCacheEntry} is appropriate by building a conditional * {@link org.apache.hc.core5.http.HttpRequest} using the variants' ETag values. * If no such values exist, the request is unmodified * * @param request the original request from the caller * @param variants * @return the wrapped request */ public T buildConditionalRequestFromVariants(final T request, final Map variants) { final T newRequest = messageCopier.create(request); // we do not support partial content so all etags are used final StringBuilder etags = new StringBuilder(); boolean first = true; for(final String etag : variants.keySet()) { if (!first) { etags.append(","); } first = false; etags.append(etag); } newRequest.setHeader(HeaderConstants.IF_NONE_MATCH, etags.toString()); return newRequest; } /** * Returns a request to unconditionally validate a cache entry with * the origin. In certain cases (due to multiple intervening caches) * our cache may actually receive a response to a normal conditional * validation where the Date header is actually older than that of * our current cache entry. In this case, the protocol recommendation * is to retry the validation and force syncup with the origin. * @param request client request we are trying to satisfy * @return an unconditional validation request */ public T buildUnconditionalRequest(final T request) { final T newRequest = messageCopier.create(request); newRequest.addHeader(HeaderConstants.CACHE_CONTROL,HeaderConstants.CACHE_CONTROL_NO_CACHE); newRequest.addHeader(HeaderConstants.PRAGMA,HeaderConstants.CACHE_CONTROL_NO_CACHE); newRequest.removeHeaders(HeaderConstants.IF_RANGE); newRequest.removeHeaders(HeaderConstants.IF_MATCH); newRequest.removeHeaders(HeaderConstants.IF_NONE_MATCH); newRequest.removeHeaders(HeaderConstants.IF_UNMODIFIED_SINCE); newRequest.removeHeaders(HeaderConstants.IF_MODIFIED_SINCE); return newRequest; } } DateSupport.java000066400000000000000000000103631434266521000367620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MessageHeaders; /** * HTTP cache date support utilities. * * @since 5.2 */ @Internal public final class DateSupport { /** * Tests if the first message is after (newer) than second one * using the given message header for comparison. * * @param message1 the first message * @param message2 the second message * @param headerName header name * * @return {@code true} if both messages contain a header with the given name * and the value of the header from the first message is newer that of * the second message. * * @since 5.0 */ public static boolean isAfter( final MessageHeaders message1, final MessageHeaders message2, final String headerName) { if (message1 != null && message2 != null) { final Header dateHeader1 = message1.getFirstHeader(headerName); if (dateHeader1 != null) { final Header dateHeader2 = message2.getFirstHeader(headerName); if (dateHeader2 != null) { final Instant date1 = DateUtils.parseStandardDate(dateHeader1.getValue()); if (date1 != null) { final Instant date2 = DateUtils.parseStandardDate(dateHeader2.getValue()); if (date2 != null) { return date1.isAfter(date2); } } } } } return false; } /** * Tests if the first message is before (older) than the second one * using the given message header for comparison. * * @param message1 the first message * @param message2 the second message * @param headerName header name * * @return {@code true} if both messages contain a header with the given name * and the value of the header from the first message is older that of * the second message. * * @since 5.0 */ public static boolean isBefore( final MessageHeaders message1, final MessageHeaders message2, final String headerName) { if (message1 != null && message2 != null) { final Header dateHeader1 = message1.getFirstHeader(headerName); if (dateHeader1 != null) { final Header dateHeader2 = message2.getFirstHeader(headerName); if (dateHeader2 != null) { final Instant date1 = DateUtils.parseStandardDate(dateHeader1.getValue()); if (date1 != null) { final Instant date2 = DateUtils.parseStandardDate(dateHeader2.getValue()); if (date2 != null) { return date1.isBefore(date2); } } } } } return false; } } DefaultAsyncCacheInvalidator.java000066400000000000000000000263531434266521000422210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.hc.client5.http.cache.HttpAsyncCacheInvalidator; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Given a particular HTTP request / response pair, flush any cache entries * that this exchange would invalidate. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public class DefaultAsyncCacheInvalidator extends CacheInvalidatorBase implements HttpAsyncCacheInvalidator { public static final DefaultAsyncCacheInvalidator INSTANCE = new DefaultAsyncCacheInvalidator(); private static final Logger LOG = LoggerFactory.getLogger(DefaultAsyncCacheInvalidator.class); private void removeEntry(final HttpAsyncCacheStorage storage, final String cacheKey) { storage.removeEntry(cacheKey, new FutureCallback() { @Override public void completed(final Boolean result) { if (LOG.isDebugEnabled()) { if (result.booleanValue()) { LOG.debug("Cache entry with key {} successfully flushed", cacheKey); } else { LOG.debug("Cache entry with key {} could not be flushed", cacheKey); } } } @Override public void failed(final Exception ex) { if (LOG.isWarnEnabled()) { LOG.warn("Unable to flush cache entry with key {}", cacheKey, ex); } } @Override public void cancelled() { } }); } @Override public Cancellable flushCacheEntriesInvalidatedByRequest( final HttpHost host, final HttpRequest request, final Resolver cacheKeyResolver, final HttpAsyncCacheStorage storage, final FutureCallback callback) { final String s = HttpCacheSupport.getRequestUri(request, host); final URI uri = HttpCacheSupport.normalizeQuietly(s); final String cacheKey = uri != null ? cacheKeyResolver.resolve(uri) : s; return storage.getEntry(cacheKey, new FutureCallback() { @Override public void completed(final HttpCacheEntry parentEntry) { if (requestShouldNotBeCached(request) || shouldInvalidateHeadCacheEntry(request, parentEntry)) { if (parentEntry != null) { if (LOG.isDebugEnabled()) { LOG.debug("Invalidating parentEntry cache entry with key {}", cacheKey); } for (final String variantURI : parentEntry.getVariantMap().values()) { removeEntry(storage, variantURI); } removeEntry(storage, cacheKey); } if (uri != null) { if (LOG.isWarnEnabled()) { LOG.warn("{} is not a valid URI", s); } final Header clHdr = request.getFirstHeader(HttpHeaders.CONTENT_LOCATION); if (clHdr != null) { final URI contentLocation = HttpCacheSupport.normalizeQuietly(clHdr.getValue()); if (contentLocation != null) { if (!flushAbsoluteUriFromSameHost(uri, contentLocation, cacheKeyResolver, storage)) { flushRelativeUriFromSameHost(uri, contentLocation, cacheKeyResolver, storage); } } } final Header lHdr = request.getFirstHeader(HttpHeaders.LOCATION); if (lHdr != null) { final URI location = HttpCacheSupport.normalizeQuietly(lHdr.getValue()); if (location != null) { flushAbsoluteUriFromSameHost(uri, location, cacheKeyResolver, storage); } } } } callback.completed(Boolean.TRUE); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } private void flushRelativeUriFromSameHost( final URI requestUri, final URI uri, final Resolver cacheKeyResolver, final HttpAsyncCacheStorage storage) { final URI resolvedUri = uri != null ? URIUtils.resolve(requestUri, uri) : null; if (resolvedUri != null && isSameHost(requestUri, resolvedUri)) { removeEntry(storage, cacheKeyResolver.resolve(resolvedUri)); } } private boolean flushAbsoluteUriFromSameHost( final URI requestUri, final URI uri, final Resolver cacheKeyResolver, final HttpAsyncCacheStorage storage) { if (uri != null && isSameHost(requestUri, uri)) { removeEntry(storage, cacheKeyResolver.resolve(uri)); return true; } return false; } @Override public Cancellable flushCacheEntriesInvalidatedByExchange( final HttpHost host, final HttpRequest request, final HttpResponse response, final Resolver cacheKeyResolver, final HttpAsyncCacheStorage storage, final FutureCallback callback) { final int status = response.getCode(); if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) { final String s = HttpCacheSupport.getRequestUri(request, host); final URI requestUri = HttpCacheSupport.normalizeQuietly(s); if (requestUri != null) { final List cacheKeys = new ArrayList<>(2); final URI contentLocation = getContentLocationURI(requestUri, response); if (contentLocation != null && isSameHost(requestUri, contentLocation)) { cacheKeys.add(cacheKeyResolver.resolve(contentLocation)); } final URI location = getLocationURI(requestUri, response); if (location != null && isSameHost(requestUri, location)) { cacheKeys.add(cacheKeyResolver.resolve(location)); } if (cacheKeys.size() == 1) { final String key = cacheKeys.get(0); storage.getEntry(key, new FutureCallback() { @Override public void completed(final HttpCacheEntry entry) { if (entry != null) { // do not invalidate if response is strictly older than entry // or if the etags match if (!responseDateOlderThanEntryDate(response, entry) && responseAndEntryEtagsDiffer(response, entry)) { removeEntry(storage, key); } } callback.completed(Boolean.TRUE); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } else if (cacheKeys.size() > 1) { storage.getEntries(cacheKeys, new FutureCallback>() { @Override public void completed(final Map resultMap) { for (final Map.Entry resultEntry: resultMap.entrySet()) { // do not invalidate if response is strictly older than entry // or if the etags match final String key = resultEntry.getKey(); final HttpCacheEntry entry = resultEntry.getValue(); if (!responseDateOlderThanEntryDate(response, entry) && responseAndEntryEtagsDiffer(response, entry)) { removeEntry(storage, key); } } callback.completed(Boolean.TRUE); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } } } callback.completed(Boolean.TRUE); return Operations.nonCancellable(); } } DefaultAsyncCacheRevalidator.java000066400000000000000000000144541434266521000422200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class used for asynchronous revalidations to be used when the {@code stale-while-revalidate} * directive is present */ class DefaultAsyncCacheRevalidator extends CacheRevalidatorBase { private static final Logger LOG = LoggerFactory.getLogger(DefaultAsyncCacheRevalidator.class); interface RevalidationCall { void execute(AsyncExecCallback asyncExecCallback); } static class InternalScheduledExecutor implements ScheduledExecutor { private final ScheduledExecutor executor; InternalScheduledExecutor(final ScheduledExecutor executor) { this.executor = executor; } @Override public Future schedule(final Runnable command, final TimeValue timeValue) throws RejectedExecutionException { if (timeValue.toMilliseconds() <= 0) { command.run(); return new Operations.CompletedFuture(null); } return executor.schedule(command, timeValue); } @Override public void shutdown() { executor.shutdown(); } @Override public void awaitTermination(final Timeout timeout) throws InterruptedException { executor.awaitTermination(timeout); } } private final CacheKeyGenerator cacheKeyGenerator; /** * Create DefaultCacheRevalidator which will make ache revalidation requests * using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutor}. */ public DefaultAsyncCacheRevalidator( final ScheduledExecutor scheduledExecutor, final SchedulingStrategy schedulingStrategy) { super(new InternalScheduledExecutor(scheduledExecutor), schedulingStrategy); this.cacheKeyGenerator = CacheKeyGenerator.INSTANCE; } /** * Create CacheValidator which will make ache revalidation requests * using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutorService}. */ public DefaultAsyncCacheRevalidator( final ScheduledExecutorService executorService, final SchedulingStrategy schedulingStrategy) { this(wrap(executorService), schedulingStrategy); } /** * Schedules an asynchronous re-validation */ public void revalidateCacheEntry( final String cacheKey , final AsyncExecCallback asyncExecCallback, final RevalidationCall call) { scheduleRevalidation(cacheKey, () -> call.execute(new AsyncExecCallback() { private final AtomicReference responseRef = new AtomicReference<>(); @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { responseRef.set(response); return asyncExecCallback.handleResponse(response, entityDetails); } @Override public void handleInformationResponse( final HttpResponse response) throws HttpException, IOException { asyncExecCallback.handleInformationResponse(response); } @Override public void completed() { final HttpResponse httpResponse = responseRef.getAndSet(null); if (httpResponse != null && httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) { jobSuccessful(cacheKey); } else { jobFailed(cacheKey); } asyncExecCallback.completed(); } @Override public void failed(final Exception cause) { if (cause instanceof IOException) { LOG.debug("Asynchronous revalidation failed due to I/O error", cause); } else if (cause instanceof HttpException) { LOG.error("HTTP protocol exception during asynchronous revalidation", cause); } else { LOG.error("Unexpected runtime exception thrown during asynchronous revalidation", cause); } try { jobFailed(cacheKey); } finally { asyncExecCallback.failed(cause); } } })); } } DefaultCacheInvalidator.java000066400000000000000000000175241434266521000412230ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.net.URI; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheInvalidator; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Given a particular HTTP request / response pair, flush any cache entries * that this exchange would invalidate. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public class DefaultCacheInvalidator extends CacheInvalidatorBase implements HttpCacheInvalidator { public static final DefaultCacheInvalidator INSTANCE = new DefaultCacheInvalidator(); private static final Logger LOG = LoggerFactory.getLogger(DefaultCacheInvalidator.class); private HttpCacheEntry getEntry(final HttpCacheStorage storage, final String cacheKey) { try { return storage.getEntry(cacheKey); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("Unable to get cache entry with key {}", cacheKey, ex); } return null; } } private void removeEntry(final HttpCacheStorage storage, final String cacheKey) { try { storage.removeEntry(cacheKey); } catch (final ResourceIOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("Unable to flush cache entry with key {}", cacheKey, ex); } } } @Override public void flushCacheEntriesInvalidatedByRequest( final HttpHost host, final HttpRequest request, final Resolver cacheKeyResolver, final HttpCacheStorage storage) { final String s = HttpCacheSupport.getRequestUri(request, host); final URI uri = HttpCacheSupport.normalizeQuietly(s); final String cacheKey = uri != null ? cacheKeyResolver.resolve(uri) : s; final HttpCacheEntry parent = getEntry(storage, cacheKey); if (requestShouldNotBeCached(request) || shouldInvalidateHeadCacheEntry(request, parent)) { if (parent != null) { if (LOG.isDebugEnabled()) { LOG.debug("Invalidating parent cache entry with key {}", cacheKey); } for (final String variantURI : parent.getVariantMap().values()) { removeEntry(storage, variantURI); } removeEntry(storage, cacheKey); } if (uri != null) { if (LOG.isWarnEnabled()) { LOG.warn("{} is not a valid URI", s); } final Header clHdr = request.getFirstHeader(HttpHeaders.CONTENT_LOCATION); if (clHdr != null) { final URI contentLocation = HttpCacheSupport.normalizeQuietly(clHdr.getValue()); if (contentLocation != null) { if (!flushAbsoluteUriFromSameHost(uri, contentLocation, cacheKeyResolver, storage)) { flushRelativeUriFromSameHost(uri, contentLocation, cacheKeyResolver, storage); } } } final Header lHdr = request.getFirstHeader(HttpHeaders.LOCATION); if (lHdr != null) { final URI location = HttpCacheSupport.normalizeQuietly(lHdr.getValue()); if (location != null) { flushAbsoluteUriFromSameHost(uri, location, cacheKeyResolver, storage); } } } } } private void flushRelativeUriFromSameHost( final URI requestUri, final URI uri, final Resolver cacheKeyResolver, final HttpCacheStorage storage) { final URI resolvedUri = uri != null ? URIUtils.resolve(requestUri, uri) : null; if (resolvedUri != null && isSameHost(requestUri, resolvedUri)) { removeEntry(storage, cacheKeyResolver.resolve(resolvedUri)); } } private boolean flushAbsoluteUriFromSameHost( final URI requestUri, final URI uri, final Resolver cacheKeyResolver, final HttpCacheStorage storage) { if (uri != null && isSameHost(requestUri, uri)) { removeEntry(storage, cacheKeyResolver.resolve(uri)); return true; } return false; } @Override public void flushCacheEntriesInvalidatedByExchange( final HttpHost host, final HttpRequest request, final HttpResponse response, final Resolver cacheKeyResolver, final HttpCacheStorage storage) { final int status = response.getCode(); if (status < 200 || status > 299) { return; } final String s = HttpCacheSupport.getRequestUri(request, host); final URI uri = HttpCacheSupport.normalizeQuietly(s); if (uri == null) { return; } final URI contentLocation = getContentLocationURI(uri, response); if (contentLocation != null && isSameHost(uri, contentLocation)) { flushLocationCacheEntry(response, contentLocation, storage, cacheKeyResolver); } final URI location = getLocationURI(uri, response); if (location != null && isSameHost(uri, location)) { flushLocationCacheEntry(response, location, storage, cacheKeyResolver); } } private void flushLocationCacheEntry( final HttpResponse response, final URI location, final HttpCacheStorage storage, final Resolver cacheKeyResolver) { final String cacheKey = cacheKeyResolver.resolve(location); final HttpCacheEntry entry = getEntry(storage, cacheKey); if (entry != null) { // do not invalidate if response is strictly older than entry // or if the etags match if (!responseDateOlderThanEntryDate(response, entry) && responseAndEntryEtagsDiffer(response, entry)) { removeEntry(storage, cacheKey); } } } } DefaultCacheRevalidator.java000066400000000000000000000075031434266521000412170ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.util.concurrent.ScheduledExecutorService; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class used for asynchronous revalidations to be used when * the {@code stale-while-revalidate} directive is present */ class DefaultCacheRevalidator extends CacheRevalidatorBase { private static final Logger LOG = LoggerFactory.getLogger(DefaultCacheRevalidator.class); interface RevalidationCall { ClassicHttpResponse execute() throws IOException, HttpException; } /** * Create DefaultCacheRevalidator which will make ache revalidation requests * using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutor}. */ public DefaultCacheRevalidator( final CacheRevalidatorBase.ScheduledExecutor scheduledExecutor, final SchedulingStrategy schedulingStrategy) { super(scheduledExecutor, schedulingStrategy); } /** * Create CacheValidator which will make ache revalidation requests * using the supplied {@link SchedulingStrategy} and {@link ScheduledExecutorService}. */ public DefaultCacheRevalidator( final ScheduledExecutorService scheduledThreadPoolExecutor, final SchedulingStrategy schedulingStrategy) { this(wrap(scheduledThreadPoolExecutor), schedulingStrategy); } /** * Schedules an asynchronous re-validation */ public void revalidateCacheEntry( final String cacheKey, final RevalidationCall call) { scheduleRevalidation(cacheKey, () -> { try (ClassicHttpResponse httpResponse = call.execute()) { if (httpResponse.getCode() < HttpStatus.SC_SERVER_ERROR && !isStale(httpResponse)) { jobSuccessful(cacheKey); } else { jobFailed(cacheKey); } } catch (final IOException ex) { jobFailed(cacheKey); LOG.debug("Asynchronous revalidation failed due to I/O error", ex); } catch (final HttpException ex) { jobFailed(cacheKey); LOG.error("HTTP protocol exception during asynchronous revalidation", ex); } catch (final RuntimeException ex) { jobFailed(cacheKey); LOG.error("Unexpected runtime exception thrown during asynchronous revalidation", ex); } }); } } FileResource.java000066400000000000000000000071211434266521000370750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; /** * Cache resource backed by a file. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.SAFE) public class FileResource extends Resource { private static final long serialVersionUID = 4132244415919043397L; private final AtomicReference fileRef; private final long len; public FileResource(final File file) { super(); Args.notNull(file, "File"); this.fileRef = new AtomicReference<>(file); this.len = file.length(); } File getFile() { return this.fileRef.get(); } @Override public byte[] get() throws ResourceIOException { final File file = this.fileRef.get(); if (file == null) { throw new ResourceIOException("Resource already disposed"); } try (final InputStream in = new FileInputStream(file)) { final ByteArrayBuffer buf = new ByteArrayBuffer(1024); final byte[] tmp = new byte[2048]; int len; while ((len = in.read(tmp)) != -1) { buf.append(tmp, 0, len); } return buf.toByteArray(); } catch (final IOException ex) { throw new ResourceIOException(ex.getMessage(), ex); } } @Override public InputStream getInputStream() throws ResourceIOException { final File file = this.fileRef.get(); if (file != null) { try { return new FileInputStream(file); } catch (final FileNotFoundException ex) { throw new ResourceIOException(ex.getMessage(), ex); } } throw new ResourceIOException("Resource already disposed"); } @Override public long length() { return len; } @Override public void dispose() { final File file = this.fileRef.getAndSet(null); if (file != null) { file.delete(); } } } FileResourceFactory.java000066400000000000000000000114171434266521000404300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Generates {@link Resource} instances whose body is stored in a temporary file. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public class FileResourceFactory implements ResourceFactory { private final File cacheDir; private final BasicIdGenerator idgen; public FileResourceFactory(final File cacheDir) { super(); this.cacheDir = cacheDir; this.idgen = new BasicIdGenerator(); } private File generateUniqueCacheFile(final String requestId) { final StringBuilder buffer = new StringBuilder(); this.idgen.generate(buffer); buffer.append('.'); final int len = Math.min(requestId.length(), 100); for (int i = 0; i < len; i++) { final char ch = requestId.charAt(i); if (Character.isLetterOrDigit(ch) || ch == '.') { buffer.append(ch); } else { buffer.append('-'); } } return new File(this.cacheDir, buffer.toString()); } @Override public Resource generate( final String requestId, final byte[] content, final int off, final int len) throws ResourceIOException { Args.notNull(requestId, "Request id"); final File file = generateUniqueCacheFile(requestId); try (FileOutputStream outStream = new FileOutputStream(file)) { if (content != null) { outStream.write(content, off, len); } } catch (final IOException ex) { throw new ResourceIOException(ex.getMessage(), ex); } return new FileResource(file); } @Override public Resource generate(final String requestId, final byte[] content) throws ResourceIOException { Args.notNull(content, "Content"); return generate(requestId, content, 0, content.length); } @Override public Resource copy( final String requestId, final Resource resource) throws ResourceIOException { final File file = generateUniqueCacheFile(requestId); try { if (resource instanceof FileResource) { try (final RandomAccessFile srcFile = new RandomAccessFile(((FileResource) resource).getFile(), "r"); final RandomAccessFile dstFile = new RandomAccessFile(file, "rw"); final FileChannel src = srcFile.getChannel(); final FileChannel dst = dstFile.getChannel()) { src.transferTo(0, srcFile.length(), dst); } } else { try (final FileOutputStream out = new FileOutputStream(file); final InputStream in = resource.getInputStream()) { final byte[] buf = new byte[2048]; int len; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } } } } catch (final IOException ex) { throw new ResourceIOException(ex.getMessage(), ex); } return new FileResource(file); } } HeapResource.java000066400000000000000000000046771434266521000371100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Cache resource backed by a byte array on the heap. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class HeapResource extends Resource { private static final long serialVersionUID = -2078599905620463394L; private final AtomicReference arrayRef; public HeapResource(final byte[] b) { super(); this.arrayRef = new AtomicReference<>(b); } @Override public byte[] get() throws ResourceIOException { final byte[] byteArray = this.arrayRef.get(); if (byteArray != null) { return byteArray; } throw new ResourceIOException("Resource already disposed"); } @Override public long length() { final byte[] byteArray = this.arrayRef.get(); if (byteArray != null) { return byteArray.length; } else { return -1; } } @Override public void dispose() { this.arrayRef.set(null); } } HeapResourceFactory.java000066400000000000000000000047511434266521000404310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Generates {@link Resource} instances stored entirely in heap. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public class HeapResourceFactory implements ResourceFactory { public static final HeapResourceFactory INSTANCE = new HeapResourceFactory(); @Override public Resource generate( final String requestId, final byte[] content, final int off, final int len) { final byte[] copy = new byte[len]; System.arraycopy(content, off, copy, 0, len); return new HeapResource(copy); } @Override public Resource generate(final String requestId, final byte[] content) { return new HeapResource(content != null ? content.clone() : null); } @Override public Resource copy( final String requestId, final Resource resource) throws ResourceIOException { Args.notNull(resource, "Resource"); return new HeapResource(resource.get()); } } HttpAsyncCache.java000066400000000000000000000105001434266521000373420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.util.ByteArrayBuffer; interface HttpAsyncCache { String generateKey (HttpHost host, HttpRequest request, HttpCacheEntry cacheEntry); /** * Clear all matching {@link HttpCacheEntry}s. */ Cancellable flushCacheEntriesFor( HttpHost host, HttpRequest request, FutureCallback callback); /** * Flush {@link HttpCacheEntry}s invalidated by the given request */ Cancellable flushCacheEntriesInvalidatedByRequest( HttpHost host, HttpRequest request, FutureCallback callback); /** * Flush {@link HttpCacheEntry}s invalidated by the given message exchange. */ Cancellable flushCacheEntriesInvalidatedByExchange( HttpHost host, HttpRequest request, HttpResponse response, FutureCallback callback); /** * Retrieve matching {@link HttpCacheEntry} from the cache if it exists */ Cancellable getCacheEntry( HttpHost host, HttpRequest request, FutureCallback callback); /** * Retrieve all variants from the cache, if there are no variants then an empty */ Cancellable getVariantCacheEntriesWithEtags( HttpHost host, HttpRequest request, FutureCallback> callback); /** * Store a {@link HttpResponse} in the cache if possible, and return */ Cancellable createCacheEntry( HttpHost host, HttpRequest request, HttpResponse originResponse, ByteArrayBuffer content, Instant requestSent, Instant responseReceived, FutureCallback callback); /** * Update a {@link HttpCacheEntry} using a 304 {@link HttpResponse}. */ Cancellable updateCacheEntry( HttpHost host, HttpRequest request, HttpCacheEntry stale, HttpResponse originResponse, Instant requestSent, Instant responseReceived, FutureCallback callback); /** * Update a specific {@link HttpCacheEntry} representing a cached variant * using a 304 {@link HttpResponse}. */ Cancellable updateVariantCacheEntry( HttpHost host, HttpRequest request, HttpResponse originResponse, Variant variant, Instant requestSent, Instant responseReceived, FutureCallback callback); /** * Specifies cache should reuse the given cached variant to satisfy * requests whose varying headers match those of the given client request. */ Cancellable reuseVariantEntryFor( HttpHost host, HttpRequest req, Variant variant, FutureCallback callback); } HttpByteArrayCacheEntrySerializer.java000066400000000000000000000460161434266521000432560ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.time.Instant; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.impl.io.AbstractMessageParser; import org.apache.hc.core5.http.impl.io.AbstractMessageWriter; import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParser; import org.apache.hc.core5.http.impl.io.SessionInputBufferImpl; import org.apache.hc.core5.http.impl.io.SessionOutputBufferImpl; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicLineFormatter; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TimeValue; /** * Cache serializer and deserializer that uses an HTTP-like format. * * Existing libraries for reading and writing HTTP are used, and metadata is encoded into HTTP * pseudo-headers for storage. */ @Experimental public class HttpByteArrayCacheEntrySerializer implements HttpCacheEntrySerializer { public static final HttpByteArrayCacheEntrySerializer INSTANCE = new HttpByteArrayCacheEntrySerializer(); private static final String SC_CACHE_ENTRY_PREFIX = "hc-"; private static final String SC_HEADER_NAME_STORAGE_KEY = SC_CACHE_ENTRY_PREFIX + "sk"; private static final String SC_HEADER_NAME_RESPONSE_DATE = SC_CACHE_ENTRY_PREFIX + "resp-date"; private static final String SC_HEADER_NAME_REQUEST_DATE = SC_CACHE_ENTRY_PREFIX + "req-date"; private static final String SC_HEADER_NAME_NO_CONTENT = SC_CACHE_ENTRY_PREFIX + "no-content"; private static final String SC_HEADER_NAME_VARIANT_MAP_KEY = SC_CACHE_ENTRY_PREFIX + "varmap-key"; private static final String SC_HEADER_NAME_VARIANT_MAP_VALUE = SC_CACHE_ENTRY_PREFIX + "varmap-val"; private static final String SC_CACHE_ENTRY_PRESERVE_PREFIX = SC_CACHE_ENTRY_PREFIX + "esc-"; private static final int BUFFER_SIZE = 8192; public HttpByteArrayCacheEntrySerializer() { } @Override public byte[] serialize(final HttpCacheStorageEntry httpCacheEntry) throws ResourceIOException { if (httpCacheEntry.getKey() == null) { throw new IllegalStateException("Cannot serialize cache object with null storage key"); } // content doesn't need null-check because it's validated in the HttpCacheStorageEntry constructor // Fake HTTP request, required by response generator // Use request method from httpCacheEntry, but as far as I can tell it will only ever return "GET". final HttpRequest httpRequest = new BasicHttpRequest(httpCacheEntry.getContent().getRequestMethod(), "/"); final CacheValidityPolicy cacheValidityPolicy = new NoAgeCacheValidityPolicy(); final CachedHttpResponseGenerator cachedHttpResponseGenerator = new CachedHttpResponseGenerator(cacheValidityPolicy); final SimpleHttpResponse httpResponse = cachedHttpResponseGenerator.generateResponse(httpRequest, httpCacheEntry.getContent()); try(final ByteArrayOutputStream out = new ByteArrayOutputStream()) { escapeHeaders(httpResponse); addMetadataPseudoHeaders(httpResponse, httpCacheEntry); final byte[] bodyBytes = httpResponse.getBodyBytes(); final int resourceLength; if (bodyBytes == null) { // This means no content, for example a 204 response httpResponse.addHeader(SC_HEADER_NAME_NO_CONTENT, Boolean.TRUE.toString()); resourceLength = 0; } else { resourceLength = bodyBytes.length; } // Use the default, ASCII-only encoder for HTTP protocol and header values. // It's the only thing that's widely used, and it's not worth it to support anything else. final SessionOutputBufferImpl outputBuffer = new SessionOutputBufferImpl(BUFFER_SIZE); final AbstractMessageWriter httpResponseWriter = makeHttpResponseWriter(outputBuffer); httpResponseWriter.write(httpResponse, outputBuffer, out); outputBuffer.flush(out); final byte[] headerBytes = out.toByteArray(); final byte[] bytes = new byte[headerBytes.length + resourceLength]; System.arraycopy(headerBytes, 0, bytes, 0, headerBytes.length); if (resourceLength > 0) { System.arraycopy(bodyBytes, 0, bytes, headerBytes.length, resourceLength); } return bytes; } catch(final IOException|HttpException e) { throw new ResourceIOException("Exception while serializing cache entry", e); } } @Override public HttpCacheStorageEntry deserialize(final byte[] serializedObject) throws ResourceIOException { try (final InputStream in = makeByteArrayInputStream(serializedObject); final ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(serializedObject.length) // this is bigger than necessary but will save us from reallocating ) { final SessionInputBufferImpl inputBuffer = new SessionInputBufferImpl(BUFFER_SIZE); final AbstractMessageParser responseParser = makeHttpResponseParser(); final ClassicHttpResponse response = responseParser.parse(inputBuffer, in); // Extract metadata pseudo-headers final String storageKey = getCachePseudoHeaderAndRemove(response, SC_HEADER_NAME_STORAGE_KEY); final Instant requestDate = getCachePseudoHeaderDateAndRemove(response, SC_HEADER_NAME_REQUEST_DATE); final Instant responseDate = getCachePseudoHeaderDateAndRemove(response, SC_HEADER_NAME_RESPONSE_DATE); final boolean noBody = getCachePseudoHeaderBooleanAndRemove(response, SC_HEADER_NAME_NO_CONTENT); final Map variantMap = getVariantMapPseudoHeadersAndRemove(response); unescapeHeaders(response); final Resource resource; if (noBody) { // This means no content, for example a 204 response resource = null; } else { copyBytes(inputBuffer, in, bytesOut); resource = new HeapResource(bytesOut.toByteArray()); } final HttpCacheEntry httpCacheEntry = new HttpCacheEntry( requestDate, responseDate, response.getCode(), response.getHeaders(), resource, variantMap ); return new HttpCacheStorageEntry(storageKey, httpCacheEntry); } catch (final IOException|HttpException e) { throw new ResourceIOException("Error deserializing cache entry", e); } } /** * Helper method to make a new HTTP response writer. *

* Useful to override for testing. * * @param outputBuffer Output buffer to write to * @return HTTP response writer to write to */ protected AbstractMessageWriter makeHttpResponseWriter(final SessionOutputBuffer outputBuffer) { return new SimpleHttpResponseWriter(); } /** * Helper method to make a new ByteArrayInputStream. *

* Useful to override for testing. * * @param bytes Bytes to read from the stream * @return Stream to read the bytes from */ protected InputStream makeByteArrayInputStream(final byte[] bytes) { return new ByteArrayInputStream(bytes); } /** * Helper method to make a new HTTP Response parser. *

* Useful to override for testing. * * @return HTTP response parser */ protected AbstractMessageParser makeHttpResponseParser() { return new DefaultHttpResponseParser(); } /** * Modify the given response to escape any header names that start with the prefix we use for our own pseudo-headers, * prefixing them with an escape sequence we can use to recover them later. * * @param httpResponse HTTP response object to escape headers in * @see #unescapeHeaders(HttpResponse) for the corresponding un-escaper. */ private static void escapeHeaders(final HttpResponse httpResponse) { final Header[] headers = httpResponse.getHeaders(); for (final Header header : headers) { if (header.getName().startsWith(SC_CACHE_ENTRY_PREFIX)) { httpResponse.removeHeader(header); httpResponse.addHeader(SC_CACHE_ENTRY_PRESERVE_PREFIX + header.getName(), header.getValue()); } } } /** * Modify the given response to remove escaping from any header names we escaped before saving. * * @param httpResponse HTTP response object to un-escape headers in * @see #unescapeHeaders(HttpResponse) for the corresponding escaper */ private void unescapeHeaders(final HttpResponse httpResponse) { final Header[] headers = httpResponse.getHeaders(); for (final Header header : headers) { if (header.getName().startsWith(SC_CACHE_ENTRY_PRESERVE_PREFIX)) { httpResponse.removeHeader(header); httpResponse.addHeader(header.getName().substring(SC_CACHE_ENTRY_PRESERVE_PREFIX.length()), header.getValue()); } } } /** * Modify the given response to add our own cache metadata as pseudo-headers. * * @param httpResponse HTTP response object to add pseudo-headers to */ private void addMetadataPseudoHeaders(final HttpResponse httpResponse, final HttpCacheStorageEntry httpCacheEntry) { httpResponse.addHeader(SC_HEADER_NAME_STORAGE_KEY, httpCacheEntry.getKey()); httpResponse.addHeader(SC_HEADER_NAME_RESPONSE_DATE, Long.toString(httpCacheEntry.getContent().getResponseInstant().toEpochMilli())); httpResponse.addHeader(SC_HEADER_NAME_REQUEST_DATE, Long.toString(httpCacheEntry.getContent().getRequestInstant().toEpochMilli())); // Encode these so map entries are stored in a pair of headers, one for key and one for value. // Header keys look like: {Accept-Encoding=gzip} // And header values like: {Accept-Encoding=gzip}https://example.com:1234/foo for (final Map.Entry entry : httpCacheEntry.getContent().getVariantMap().entrySet()) { // Headers are ordered httpResponse.addHeader(SC_HEADER_NAME_VARIANT_MAP_KEY, entry.getKey()); httpResponse.addHeader(SC_HEADER_NAME_VARIANT_MAP_VALUE, entry.getValue()); } } /** * Get the string value for a single metadata pseudo-header, and remove it from the response object. * * @param response Response object to get and remove the pseudo-header from * @param name Name of metadata pseudo-header * @return Value for metadata pseudo-header * @throws ResourceIOException if the given pseudo-header is not found */ private static String getCachePseudoHeaderAndRemove(final HttpResponse response, final String name) throws ResourceIOException { final String headerValue = getOptionalCachePseudoHeaderAndRemove(response, name); if (headerValue == null) { throw new ResourceIOException("Expected cache header '" + name + "' not found"); } return headerValue; } /** * Get the string value for a single metadata pseudo-header if it exists, and remove it from the response object. * * @param response Response object to get and remove the pseudo-header from * @param name Name of metadata pseudo-header * @return Value for metadata pseudo-header, or null if it does not exist */ private static String getOptionalCachePseudoHeaderAndRemove(final HttpResponse response, final String name) { final Header header = response.getFirstHeader(name); if (header == null) { return null; } response.removeHeader(header); return header.getValue(); } /** * Get the date value for a single metadata pseudo-header, and remove it from the response object. * * @param response Response object to get and remove the pseudo-header from * @param name Name of metadata pseudo-header * @return Value for metadata pseudo-header * @throws ResourceIOException if the given pseudo-header is not found, or contains invalid data */ private static Instant getCachePseudoHeaderDateAndRemove(final HttpResponse response, final String name) throws ResourceIOException{ final String value = getCachePseudoHeaderAndRemove(response, name); response.removeHeaders(name); try { final long timestamp = Long.parseLong(value); return Instant.ofEpochMilli(timestamp); } catch (final NumberFormatException e) { throw new ResourceIOException("Invalid value for header '" + name + "'", e); } } /** * Get the boolean value for a single metadata pseudo-header, and remove it from the response object. * * @param response Response object to get and remove the pseudo-header from * @param name Name of metadata pseudo-header * @return Value for metadata pseudo-header */ private static boolean getCachePseudoHeaderBooleanAndRemove(final ClassicHttpResponse response, final String name) { // parseBoolean does not throw any exceptions, so no try/catch required. return Boolean.parseBoolean(getOptionalCachePseudoHeaderAndRemove(response, name)); } /** * Get the variant map metadata pseudo-header, and remove it from the response object. * * @param response Response object to get and remove the pseudo-header from * @return Extracted variant map * @throws ResourceIOException if the given pseudo-header is not found, or contains invalid data */ private static Map getVariantMapPseudoHeadersAndRemove(final HttpResponse response) throws ResourceIOException { final Header[] headers = response.getHeaders(); final Map variantMap = new HashMap<>(0); String lastKey = null; for (final Header header : headers) { if (header.getName().equals(SC_HEADER_NAME_VARIANT_MAP_KEY)) { lastKey = header.getValue(); response.removeHeader(header); } else if (header.getName().equals(SC_HEADER_NAME_VARIANT_MAP_VALUE)) { if (lastKey == null) { throw new ResourceIOException("Found mismatched variant map key/value headers"); } variantMap.put(lastKey, header.getValue()); lastKey = null; response.removeHeader(header); } } if (lastKey != null) { throw new ResourceIOException("Found mismatched variant map key/value headers"); } return variantMap; } /** * Copy bytes from the given source buffer and input stream to the given output stream until end-of-file is reached. * * @param srcBuf Buffered input source * @param src Unbuffered input source * @param dest Output destination * @throws IOException if an I/O error occurs */ private static void copyBytes(final SessionInputBuffer srcBuf, final InputStream src, final OutputStream dest) throws IOException { final byte[] buf = new byte[BUFFER_SIZE]; int lastBytesRead; while ((lastBytesRead = srcBuf.read(buf, src)) != -1) { dest.write(buf, 0, lastBytesRead); } } /** * Writer for SimpleHttpResponse. * * Copied from DefaultHttpResponseWriter, but wrapping a SimpleHttpResponse instead of a ClassicHttpResponse */ // Seems like the DefaultHttpResponseWriter should be able to do this, but it doesn't seem to be able to private class SimpleHttpResponseWriter extends AbstractMessageWriter { public SimpleHttpResponseWriter() { super(BasicLineFormatter.INSTANCE); } @Override protected void writeHeadLine( final SimpleHttpResponse message, final CharArrayBuffer lineBuf) { final ProtocolVersion transportVersion = message.getVersion(); BasicLineFormatter.INSTANCE.formatStatusLine(lineBuf, new StatusLine( transportVersion != null ? transportVersion : HttpVersion.HTTP_1_1, message.getCode(), message.getReasonPhrase())); } } /** * Cache validity policy that always returns an age of {@link TimeValue#ZERO_MILLISECONDS}. * * This prevents the Age header from being written to the cache (it does not make sense to cache it), * and is the only thing the policy is used for in this case. */ private static class NoAgeCacheValidityPolicy extends CacheValidityPolicy { @Override public TimeValue getCurrentAge(final HttpCacheEntry entry, final Instant now) { return TimeValue.ZERO_MILLISECONDS; } } } HttpCache.java000066400000000000000000000074431434266521000363600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.util.ByteArrayBuffer; interface HttpCache { String generateKey (HttpHost host, HttpRequest request, HttpCacheEntry cacheEntry); /** * Clear all matching {@link HttpCacheEntry}s. */ void flushCacheEntriesFor(HttpHost host, HttpRequest request); /** * Flush {@link HttpCacheEntry}s invalidated by the given request */ void flushCacheEntriesInvalidatedByRequest(HttpHost host, HttpRequest request); /** * Flush {@link HttpCacheEntry}s invalidated by the given message exchange. */ void flushCacheEntriesInvalidatedByExchange(HttpHost host, HttpRequest request, HttpResponse response); /** * Retrieve matching {@link HttpCacheEntry} from the cache if it exists. */ HttpCacheEntry getCacheEntry(HttpHost host, HttpRequest request); /** * Retrieve all variants from the cache, if there are no variants then an empty * {@link Map} is returned */ Map getVariantCacheEntriesWithEtags(HttpHost host, HttpRequest request); /** * Store a {@link HttpResponse} in the cache if possible, and return */ HttpCacheEntry createCacheEntry( HttpHost host, HttpRequest request, HttpResponse originResponse, ByteArrayBuffer content, Instant requestSent, Instant responseReceived); /** * Update a {@link HttpCacheEntry} using a 304 {@link HttpResponse}. */ HttpCacheEntry updateCacheEntry( HttpHost host, HttpRequest request, HttpCacheEntry stale, HttpResponse originResponse, Instant requestSent, Instant responseReceived); /** * Update a specific {@link HttpCacheEntry} representing a cached variant * using a 304 {@link HttpResponse}. */ HttpCacheEntry updateVariantCacheEntry( HttpHost host, HttpRequest request, HttpResponse originResponse, Variant variant, Instant requestSent, Instant responseReceived); /** * Specifies cache should reuse the given cached variant to satisfy * requests whose varying headers match those of the given client request. */ void reuseVariantEntryFor( HttpHost host, HttpRequest request, Variant variant); } HttpCacheSupport.java000066400000000000000000000131201434266521000377420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.net.URI; import java.net.URISyntaxException; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.util.Args; /** * HTTP cache support utilities. * * @since 5.0 */ public final class HttpCacheSupport { private static final URI BASE_URI = URI.create("http://example.com/"); /** * Returns text representation of the request URI of the given {@link HttpRequest}. * This method will use {@link HttpRequest#getPath()}, {@link HttpRequest#getScheme()} and * {@link HttpRequest#getAuthority()} values when available or attributes of target * {@link HttpHost } in order to construct an absolute URI. *

* This method will not attempt to ensure validity of the resultant text representation. * * @param request the {@link HttpRequest} * @param target target host * * @return String the request URI */ public static String getRequestUri(final HttpRequest request, final HttpHost target) { Args.notNull(request, "HTTP request"); Args.notNull(target, "Target"); final StringBuilder buf = new StringBuilder(); final URIAuthority authority = request.getAuthority(); if (authority != null) { final String scheme = request.getScheme(); buf.append(scheme != null ? scheme : URIScheme.HTTP.id).append("://"); buf.append(authority.getHostName()); if (authority.getPort() >= 0) { buf.append(":").append(authority.getPort()); } } else { buf.append(target.getSchemeName()).append("://"); buf.append(target.getHostName()); if (target.getPort() >= 0) { buf.append(":").append(target.getPort()); } } final String path = request.getPath(); if (path == null) { buf.append("/"); } else { if (buf.length() > 0 && !path.startsWith("/")) { buf.append("/"); } buf.append(path); } return buf.toString(); } /** * Returns normalized representation of the request URI optimized for use as a cache key. * This method ensures the resultant URI has an explicit port in the authority component, * and explicit path component and no fragment. * * @param requestUri original request URI * @return normalized URI. * @throws URISyntaxException */ public static URI normalize(final URI requestUri) throws URISyntaxException { Args.notNull(requestUri, "URI"); final URIBuilder builder = new URIBuilder(requestUri.isAbsolute() ? URIUtils.resolve(BASE_URI, requestUri) : requestUri) ; if (builder.getHost() != null) { if (builder.getScheme() == null) { builder.setScheme(URIScheme.HTTP.id); } if (builder.getPort() <= -1) { if (URIScheme.HTTP.same(builder.getScheme())) { builder.setPort(80); } else if (URIScheme.HTTPS.same(builder.getScheme())) { builder.setPort(443); } } } builder.setFragment(null); if (builder.isPathEmpty()) { builder.setPathSegments(""); } return builder.build(); } /** * Lenient URI parser that normalizes valid {@link URI}s and returns {@code null} for malformed URIs. * @deprecated Use {@link #normalizeQuietly(String)} */ @Deprecated public static URI normalizeQuetly(final String requestUri) { if (requestUri == null) { return null; } try { return normalize(new URI(requestUri)); } catch (final URISyntaxException ex) { return null; } } /** * Lenient URI parser that normalizes valid {@link URI}s and returns {@code null} for malformed URIs. * @since 5.2 */ public static URI normalizeQuietly(final String requestUri) { if (requestUri == null) { return null; } try { return normalize(new URI(requestUri)); } catch (final URISyntaxException ex) { return null; } } } ManagedHttpCacheStorage.java000066400000000000000000000173111434266521000411550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.Closeable; import java.lang.ref.ReferenceQueue; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.client5.http.cache.HttpCacheCASOperation; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** *

* {@link HttpCacheStorage} implementation capable of deallocating resources associated with * the cache entries. *

* This cache keeps track of cache entries using * {@link java.lang.ref.PhantomReference} and maintains a collection of all resources that * are no longer in use. The cache, however, does not automatically deallocates associated * resources by invoking {@link Resource#dispose()} method. The consumer MUST periodically * call {@link #cleanResources()} method to trigger resource deallocation. The cache can be * permanently shut down using {@link #shutdown()} method. All resources associated with * the entries used by the cache will be deallocated. *

*

* This {@link HttpCacheStorage} implementation is intended for use with {@link FileResource} * and similar. *

*

* Compatibility note. Prior to version 4.4 this storage implementation used to dispose of * all resource entries upon {@link #close()}. As of version 4.4 the {@link #close()} method * disposes only of those resources that have been explicitly removed from the cache with * {@link #removeEntry(String)} method. *

*

* The {@link #shutdown()} ()} method can still be used to shut down the storage and dispose of * all resources currently managed by it. *

* * @since 4.1 */ @Contract(threading = ThreadingBehavior.SAFE) public class ManagedHttpCacheStorage implements HttpCacheStorage, Closeable { private final CacheMap entries; private final ReferenceQueue morque; private final Set resources; private final AtomicBoolean active; public ManagedHttpCacheStorage(final CacheConfig config) { super(); this.entries = new CacheMap(config.getMaxCacheEntries()); this.morque = new ReferenceQueue<>(); this.resources = new HashSet<>(); this.active = new AtomicBoolean(true); } private void ensureValidState() { if (!isActive()) { throw new IllegalStateException("Cache has been shut down"); } } private void keepResourceReference(final HttpCacheEntry entry) { final Resource resource = entry.getResource(); if (resource != null) { // Must deallocate the resource when the entry is no longer in used final ResourceReference ref = new ResourceReference(entry, this.morque); this.resources.add(ref); } } @Override public void putEntry(final String url, final HttpCacheEntry entry) throws ResourceIOException { Args.notNull(url, "URL"); Args.notNull(entry, "Cache entry"); ensureValidState(); synchronized (this) { this.entries.put(url, entry); keepResourceReference(entry); } } @Override public HttpCacheEntry getEntry(final String url) throws ResourceIOException { Args.notNull(url, "URL"); ensureValidState(); synchronized (this) { return this.entries.get(url); } } @Override public void removeEntry(final String url) throws ResourceIOException { Args.notNull(url, "URL"); ensureValidState(); synchronized (this) { // Cannot deallocate the associated resources immediately as the // cache entry may still be in use this.entries.remove(url); } } @Override public void updateEntry( final String url, final HttpCacheCASOperation casOperation) throws ResourceIOException { Args.notNull(url, "URL"); Args.notNull(casOperation, "CAS operation"); ensureValidState(); synchronized (this) { final HttpCacheEntry existing = this.entries.get(url); final HttpCacheEntry updated = casOperation.execute(existing); this.entries.put(url, updated); if (existing != updated) { keepResourceReference(updated); } } } @Override public Map getEntries(final Collection keys) throws ResourceIOException { Args.notNull(keys, "Key"); final Map resultMap = new HashMap<>(keys.size()); for (final String key: keys) { final HttpCacheEntry entry = getEntry(key); if (entry != null) { resultMap.put(key, entry); } } return resultMap; } public void cleanResources() { if (isActive()) { ResourceReference ref; while ((ref = (ResourceReference) this.morque.poll()) != null) { synchronized (this) { this.resources.remove(ref); } ref.getResource().dispose(); } } } public void shutdown() { if (compareAndSet()) { synchronized (this) { this.entries.clear(); for (final ResourceReference ref: this.resources) { ref.getResource().dispose(); } this.resources.clear(); while (this.morque.poll() != null) { } } } } @Override public void close() { if (compareAndSet()) { synchronized (this) { ResourceReference ref; while ((ref = (ResourceReference) this.morque.poll()) != null) { this.resources.remove(ref); ref.getResource().dispose(); } } } } /** * Check if the cache is still active and has not shut down. * * @return {@code true} if the cache is active, otherwise return {@code false}. * @since 5.2 */ public boolean isActive() { return active.get(); } private boolean compareAndSet(){ return this.active.compareAndSet(true, false); } } NoopCacheEntrySerializer.java000066400000000000000000000042751434266521000414300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * {@link HttpCacheEntrySerializer} that uses {@link HttpCacheStorageEntry} * as its cache content representation. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class NoopCacheEntrySerializer implements HttpCacheEntrySerializer { public static final NoopCacheEntrySerializer INSTANCE = new NoopCacheEntrySerializer(); @Override public HttpCacheStorageEntry serialize(final HttpCacheStorageEntry cacheEntry) throws ResourceIOException { return cacheEntry; } @Override public HttpCacheStorageEntry deserialize(final HttpCacheStorageEntry cacheEntry) throws ResourceIOException { return cacheEntry; } } RequestProtocolCompliance.java000066400000000000000000000211231434266521000416510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; class RequestProtocolCompliance { private final boolean weakETagOnPutDeleteAllowed; public RequestProtocolCompliance() { super(); this.weakETagOnPutDeleteAllowed = false; } public RequestProtocolCompliance(final boolean weakETagOnPutDeleteAllowed) { super(); this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed; } private static final List disallowedWithNoCache = Arrays.asList(HeaderConstants.CACHE_CONTROL_MIN_FRESH, HeaderConstants.CACHE_CONTROL_MAX_STALE, HeaderConstants.CACHE_CONTROL_MAX_AGE); /** * Test to see if the {@link HttpRequest} is HTTP1.1 compliant or not * and if not, we can not continue. * * @param request the HttpRequest Object * @return list of {@link RequestProtocolError} */ public List requestIsFatallyNonCompliant(final HttpRequest request) { final List theErrors = new ArrayList<>(); RequestProtocolError anError = requestHasWeakETagAndRange(request); if (anError != null) { theErrors.add(anError); } if (!weakETagOnPutDeleteAllowed) { anError = requestHasWeekETagForPUTOrDELETEIfMatch(request); if (anError != null) { theErrors.add(anError); } } anError = requestContainsNoCacheDirectiveWithFieldName(request); if (anError != null) { theErrors.add(anError); } return theErrors; } /** * If the {@link HttpRequest} is non-compliant but 'fixable' we go ahead and * fix the request here. * * @param request the request to check for compliance */ public void makeRequestCompliant(final HttpRequest request) { decrementOPTIONSMaxForwardsIfGreaterThen0(request); stripOtherFreshnessDirectivesWithNoCache(request); if (requestVersionIsTooLow(request) || requestMinorVersionIsTooHighMajorVersionsMatch(request)) { request.setVersion(HttpVersion.HTTP_1_1); } } private void stripOtherFreshnessDirectivesWithNoCache(final HttpRequest request) { final List outElts = new ArrayList<>(); boolean shouldStrip = false; final Iterator it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (!disallowedWithNoCache.contains(elt.getName())) { outElts.add(elt); } if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) { shouldStrip = true; } } if (!shouldStrip) { return; } request.removeHeaders(HeaderConstants.CACHE_CONTROL); request.setHeader(HeaderConstants.CACHE_CONTROL, buildHeaderFromElements(outElts)); } private String buildHeaderFromElements(final List outElts) { final StringBuilder newHdr = new StringBuilder(); boolean first = true; for(final HeaderElement elt : outElts) { if (!first) { newHdr.append(","); } else { first = false; } newHdr.append(elt); } return newHdr.toString(); } private void decrementOPTIONSMaxForwardsIfGreaterThen0(final HttpRequest request) { if (!HeaderConstants.OPTIONS_METHOD.equals(request.getMethod())) { return; } final Header maxForwards = request.getFirstHeader(HeaderConstants.MAX_FORWARDS); if (maxForwards == null) { return; } request.removeHeaders(HeaderConstants.MAX_FORWARDS); final int currentMaxForwards = Integer.parseInt(maxForwards.getValue()); request.setHeader(HeaderConstants.MAX_FORWARDS, Integer.toString(currentMaxForwards - 1)); } protected boolean requestMinorVersionIsTooHighMajorVersionsMatch(final HttpRequest request) { final ProtocolVersion requestProtocol = request.getVersion(); if (requestProtocol == null) { return false; } if (requestProtocol.getMajor() != HttpVersion.HTTP_1_1.getMajor()) { return false; } return requestProtocol.getMinor() > HttpVersion.HTTP_1_1.getMinor(); } protected boolean requestVersionIsTooLow(final HttpRequest request) { final ProtocolVersion requestProtocol = request.getVersion(); return requestProtocol != null && requestProtocol.compareToVersion(HttpVersion.HTTP_1_1) < 0; } private RequestProtocolError requestHasWeakETagAndRange(final HttpRequest request) { // TODO: Should these be looking at all the headers marked as Range? final String method = request.getMethod(); if (!(HeaderConstants.GET_METHOD.equals(method))) { return null; } final Header range = request.getFirstHeader(HeaderConstants.RANGE); if (range == null) { return null; } final Header ifRange = request.getFirstHeader(HeaderConstants.IF_RANGE); if (ifRange == null) { return null; } final String val = ifRange.getValue(); if (val.startsWith("W/")) { return RequestProtocolError.WEAK_ETAG_AND_RANGE_ERROR; } return null; } private RequestProtocolError requestHasWeekETagForPUTOrDELETEIfMatch(final HttpRequest request) { // TODO: Should these be looking at all the headers marked as If-Match/If-None-Match? final String method = request.getMethod(); if (!(HeaderConstants.PUT_METHOD.equals(method) || HeaderConstants.DELETE_METHOD.equals(method))) { return null; } final Header ifMatch = request.getFirstHeader(HeaderConstants.IF_MATCH); if (ifMatch != null) { final String val = ifMatch.getValue(); if (val.startsWith("W/")) { return RequestProtocolError.WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR; } } else { final Header ifNoneMatch = request.getFirstHeader(HeaderConstants.IF_NONE_MATCH); if (ifNoneMatch == null) { return null; } final String val2 = ifNoneMatch.getValue(); if (val2.startsWith("W/")) { return RequestProtocolError.WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR; } } return null; } private RequestProtocolError requestContainsNoCacheDirectiveWithFieldName(final HttpRequest request) { final Iterator it = MessageSupport.iterate(request, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(elt.getName()) && elt.getValue() != null) { return RequestProtocolError.NO_CACHE_DIRECTIVE_WITH_FIELD_NAME; } } return null; } } RequestProtocolError.java000066400000000000000000000026121434266521000406720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; enum RequestProtocolError { UNKNOWN, BODY_BUT_NO_LENGTH_ERROR, WEAK_ETAG_ON_PUTDELETE_METHOD_ERROR, WEAK_ETAG_AND_RANGE_ERROR, NO_CACHE_DIRECTIVE_WITH_FIELD_NAME } ResourceReference.java000066400000000000000000000040211434266521000401100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.core5.util.Args; class ResourceReference extends PhantomReference { private final Resource resource; public ResourceReference(final HttpCacheEntry entry, final ReferenceQueue q) { super(entry, q); Args.notNull(entry.getResource(), "Resource"); this.resource = entry.getResource(); } public Resource getResource() { return this.resource; } @Override public int hashCode() { return this.resource.hashCode(); } @Override public boolean equals(final Object obj) { return this.resource.equals(obj); } } ResponseCachingPolicy.java000066400000000000000000000311631434266521000407440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.MessageSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class ResponseCachingPolicy { private static final String[] AUTH_CACHEABLE_PARAMS = { "s-maxage", HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE, HeaderConstants.PUBLIC }; private final static Set CACHEABLE_STATUS_CODES = new HashSet<>(Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, HttpStatus.SC_MULTIPLE_CHOICES, HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_GONE)); private static final Logger LOG = LoggerFactory.getLogger(ResponseCachingPolicy.class); private final long maxObjectSizeBytes; private final boolean sharedCache; private final boolean neverCache1_0ResponsesWithQueryString; private final Set uncacheableStatusCodes; /** * Define a cache policy that limits the size of things that should be stored * in the cache to a maximum of {@link HttpResponse} bytes in size. * * @param maxObjectSizeBytes the size to limit items into the cache * @param sharedCache whether to behave as a shared cache (true) or a * non-shared/private cache (false) * @param neverCache1_0ResponsesWithQueryString true to never cache HTTP 1.0 responses with a query string, false * to cache if explicit cache headers are found. * @param allow303Caching if this policy is permitted to cache 303 response */ public ResponseCachingPolicy(final long maxObjectSizeBytes, final boolean sharedCache, final boolean neverCache1_0ResponsesWithQueryString, final boolean allow303Caching) { this.maxObjectSizeBytes = maxObjectSizeBytes; this.sharedCache = sharedCache; this.neverCache1_0ResponsesWithQueryString = neverCache1_0ResponsesWithQueryString; if (allow303Caching) { uncacheableStatusCodes = new HashSet<>(Collections.singletonList(HttpStatus.SC_PARTIAL_CONTENT)); } else { uncacheableStatusCodes = new HashSet<>(Arrays.asList(HttpStatus.SC_PARTIAL_CONTENT, HttpStatus.SC_SEE_OTHER)); } } /** * Determines if an HttpResponse can be cached. * * @param httpMethod What type of request was this, a GET, PUT, other? * @param response The origin response * @return {@code true} if response is cacheable */ public boolean isResponseCacheable(final String httpMethod, final HttpResponse response) { boolean cacheable = false; if (!HeaderConstants.GET_METHOD.equals(httpMethod) && !HeaderConstants.HEAD_METHOD.equals(httpMethod)) { if (LOG.isDebugEnabled()) { LOG.debug("{} method response is not cacheable", httpMethod); } return false; } final int status = response.getCode(); if (CACHEABLE_STATUS_CODES.contains(status)) { // these response codes MAY be cached cacheable = true; } else if (uncacheableStatusCodes.contains(status)) { if (LOG.isDebugEnabled()) { LOG.debug("{} response is not cacheable", status); } return false; } else if (unknownStatusCode(status)) { // a response with an unknown status code MUST NOT be // cached if (LOG.isDebugEnabled()) { LOG.debug("{} response is unknown", status); } return false; } final Header contentLength = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); if (contentLength != null) { final long contentLengthValue = Long.parseLong(contentLength.getValue()); if (contentLengthValue > this.maxObjectSizeBytes) { if (LOG.isDebugEnabled()) { LOG.debug("Response content length exceeds {}", this.maxObjectSizeBytes); } return false; } } if (response.countHeaders(HeaderConstants.AGE) > 1) { LOG.debug("Multiple Age headers"); return false; } if (response.countHeaders(HeaderConstants.EXPIRES) > 1) { LOG.debug("Multiple Expires headers"); return false; } if (response.countHeaders(HttpHeaders.DATE) > 1) { LOG.debug("Multiple Date headers"); return false; } final Instant date = DateUtils.parseStandardDate(response, HttpHeaders.DATE); if (date == null) { LOG.debug("Invalid / missing Date header"); return false; } final Iterator it = MessageSupport.iterate(response, HeaderConstants.VARY); while (it.hasNext()) { final HeaderElement elem = it.next(); if ("*".equals(elem.getName())) { if (LOG.isDebugEnabled()) { LOG.debug("Vary * found"); } return false; } } if (isExplicitlyNonCacheable(response)) { LOG.debug("Response is explicitly non-cacheable"); return false; } return cacheable || isExplicitlyCacheable(response); } private boolean unknownStatusCode(final int status) { if (status >= 100 && status <= 101) { return false; } if (status >= 200 && status <= 206) { return false; } if (status >= 300 && status <= 307) { return false; } if (status >= 400 && status <= 417) { return false; } return status < 500 || status > 505; } protected boolean isExplicitlyNonCacheable(final HttpResponse response) { final Iterator it = MessageSupport.iterate(response, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elem = it.next(); if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elem.getName()) || HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elem.getName()) || (sharedCache && HeaderConstants.PRIVATE.equals(elem.getName()))) { return true; } } return false; } protected boolean hasCacheControlParameterFrom(final HttpMessage msg, final String[] params) { final Iterator it = MessageSupport.iterate(msg, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elem = it.next(); for (final String param : params) { if (param.equalsIgnoreCase(elem.getName())) { return true; } } } return false; } protected boolean isExplicitlyCacheable(final HttpResponse response) { if (response.getFirstHeader(HeaderConstants.EXPIRES) != null) { return true; } final String[] cacheableParams = { HeaderConstants.CACHE_CONTROL_MAX_AGE, "s-maxage", HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE, HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE, HeaderConstants.PUBLIC }; return hasCacheControlParameterFrom(response, cacheableParams); } /** * Determine if the {@link HttpResponse} gotten from the origin is a * cacheable response. * * @param request the {@link HttpRequest} that generated an origin hit * @param response the {@link HttpResponse} from the origin * @return {@code true} if response is cacheable */ public boolean isResponseCacheable(final HttpRequest request, final HttpResponse response) { final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : HttpVersion.DEFAULT; if (version.compareToVersion(HttpVersion.HTTP_1_1) > 0) { if (LOG.isDebugEnabled()) { LOG.debug("Protocol version {} is non-cacheable", version); } return false; } final String[] uncacheableRequestDirectives = { HeaderConstants.CACHE_CONTROL_NO_STORE }; if (hasCacheControlParameterFrom(request,uncacheableRequestDirectives)) { LOG.debug("Response is explicitly non-cacheable per cache control directive"); return false; } if (request.getRequestUri().contains("?")) { if (neverCache1_0ResponsesWithQueryString && from1_0Origin(response)) { LOG.debug("Response is not cacheable as it had a query string"); return false; } else if (!isExplicitlyCacheable(response)) { LOG.debug("Response is not cacheable as it is missing explicit caching headers"); return false; } } if (expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(response)) { LOG.debug("Expires header less or equal to Date header and no cache control directives"); return false; } if (sharedCache) { if (request.countHeaders(HeaderConstants.AUTHORIZATION) > 0 && !hasCacheControlParameterFrom(response, AUTH_CACHEABLE_PARAMS)) { LOG.debug("Request contains private credentials"); return false; } } final String method = request.getMethod(); return isResponseCacheable(method, response); } private boolean expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(final HttpResponse response) { if (response.getFirstHeader(HeaderConstants.CACHE_CONTROL) != null) { return false; } final Header expiresHdr = response.getFirstHeader(HeaderConstants.EXPIRES); final Header dateHdr = response.getFirstHeader(HttpHeaders.DATE); if (expiresHdr == null || dateHdr == null) { return false; } final Instant expires = DateUtils.parseStandardDate(expiresHdr.getValue()); final Instant date = DateUtils.parseStandardDate(dateHdr.getValue()); if (expires == null || date == null) { return false; } return expires.equals(date) || expires.isBefore(date); } private boolean from1_0Origin(final HttpResponse response) { final Iterator it = MessageSupport.iterate(response, HeaderConstants.VIA); if (it.hasNext()) { final HeaderElement elt = it.next(); final String proto = elt.toString().split("\\s")[0]; if (proto.contains("/")) { return proto.equals("HTTP/1.0"); } else { return proto.equals("1.0"); } } final ProtocolVersion version = response.getVersion() != null ? response.getVersion() : HttpVersion.DEFAULT; return HttpVersion.HTTP_1_0.equals(version); } } ResponseProtocolCompliance.java000066400000000000000000000214701434266521000420240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.List; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.MessageSupport; class ResponseProtocolCompliance { private static final String UNEXPECTED_100_CONTINUE = "The incoming request did not contain a " + "100-continue header, but the response was a Status 100, continue."; private static final String UNEXPECTED_PARTIAL_CONTENT = "partial content was returned for a request that did not ask for it"; /** * When we get a response from a down stream server (Origin Server) * we attempt to see if it is HTTP 1.1 Compliant and if not, attempt to * make it so. * * @param originalRequest The original {@link HttpRequest} * @param request The {@link HttpRequest} that generated an origin hit and response * @param response The {@link HttpResponse} from the origin server * @throws IOException Bad things happened */ public void ensureProtocolCompliance( final HttpRequest originalRequest, final HttpRequest request, final HttpResponse response) throws IOException { requestDidNotExpect100ContinueButResponseIsOne(originalRequest, response); transferEncodingIsNotReturnedTo1_0Client(originalRequest, response); ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(request, response); ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(request, response); ensure206ContainsDateHeader(response); ensure304DoesNotContainExtraEntityHeaders(response); identityIsNotUsedInContentEncoding(response); warningsWithNonMatchingWarnDatesAreRemoved(response); } private void warningsWithNonMatchingWarnDatesAreRemoved( final HttpResponse response) { final Instant responseDate = DateUtils.parseStandardDate(response, HttpHeaders.DATE); if (responseDate == null) { return; } final Header[] warningHeaders = response.getHeaders(HeaderConstants.WARNING); if (warningHeaders == null || warningHeaders.length == 0) { return; } final List
newWarningHeaders = new ArrayList<>(); boolean modified = false; for(final Header h : warningHeaders) { for(final WarningValue wv : WarningValue.getWarningValues(h)) { final Instant warnInstant = wv.getWarnDate(); if (warnInstant == null || warnInstant.equals(responseDate)) { newWarningHeaders.add(new BasicHeader(HeaderConstants.WARNING,wv.toString())); } else { modified = true; } } } if (modified) { response.removeHeaders(HeaderConstants.WARNING); for(final Header h : newWarningHeaders) { response.addHeader(h); } } } private void identityIsNotUsedInContentEncoding(final HttpResponse response) { final Header[] hdrs = response.getHeaders(HttpHeaders.CONTENT_ENCODING); if (hdrs == null || hdrs.length == 0) { return; } final List
newHeaders = new ArrayList<>(); boolean modified = false; for (final Header h : hdrs) { final StringBuilder buf = new StringBuilder(); boolean first = true; for (final HeaderElement elt : MessageSupport.parse(h)) { if ("identity".equalsIgnoreCase(elt.getName())) { modified = true; } else { if (!first) { buf.append(","); } buf.append(elt); first = false; } } final String newHeaderValue = buf.toString(); if (!newHeaderValue.isEmpty()) { newHeaders.add(new BasicHeader(HttpHeaders.CONTENT_ENCODING, newHeaderValue)); } } if (!modified) { return; } response.removeHeaders(HttpHeaders.CONTENT_ENCODING); for (final Header h : newHeaders) { response.addHeader(h); } } private void ensure206ContainsDateHeader(final HttpResponse response) { if (response.getFirstHeader(HttpHeaders.DATE) == null) { response.addHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now())); } } private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request, final HttpResponse response) throws IOException { if (request.getFirstHeader(HeaderConstants.RANGE) != null || response.getCode() != HttpStatus.SC_PARTIAL_CONTENT) { return; } throw new ClientProtocolException(UNEXPECTED_PARTIAL_CONTENT); } private void ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(final HttpRequest request, final HttpResponse response) { if (!request.getMethod().equalsIgnoreCase(HeaderConstants.OPTIONS_METHOD)) { return; } if (response.getCode() != HttpStatus.SC_OK) { return; } if (response.getFirstHeader(HttpHeaders.CONTENT_LENGTH) == null) { response.addHeader(HttpHeaders.CONTENT_LENGTH, "0"); } } private void ensure304DoesNotContainExtraEntityHeaders(final HttpResponse response) { final String[] disallowedEntityHeaders = { HeaderConstants.ALLOW, HttpHeaders.CONTENT_ENCODING, "Content-Language", HttpHeaders.CONTENT_LENGTH, "Content-MD5", "Content-Range", HttpHeaders.CONTENT_TYPE, HeaderConstants.LAST_MODIFIED }; if (response.getCode() == HttpStatus.SC_NOT_MODIFIED) { for(final String hdr : disallowedEntityHeaders) { response.removeHeaders(hdr); } } } private void requestDidNotExpect100ContinueButResponseIsOne( final HttpRequest originalRequest, final HttpResponse response) throws IOException { if (response.getCode() != HttpStatus.SC_CONTINUE) { return; } final Header header = originalRequest.getFirstHeader(HttpHeaders.EXPECT); if (header != null && header.getValue().equalsIgnoreCase(HeaderElements.CONTINUE)) { return; } throw new ClientProtocolException(UNEXPECTED_100_CONTINUE); } private void transferEncodingIsNotReturnedTo1_0Client( final HttpRequest originalRequest, final HttpResponse response) { final ProtocolVersion version = originalRequest.getVersion() != null ? originalRequest.getVersion() : HttpVersion.DEFAULT; if (version.compareToVersion(HttpVersion.HTTP_1_1) >= 0) { return; } removeResponseTransferEncoding(response); } private void removeResponseTransferEncoding(final HttpResponse response) { response.removeHeaders("TE"); response.removeHeaders(HttpHeaders.TRANSFER_ENCODING); } } Variant.java000066400000000000000000000034111434266521000361100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.client5.http.cache.HttpCacheEntry; /** Records a set of information describing a cached variant. */ class Variant { private final String cacheKey; private final HttpCacheEntry entry; public Variant(final String cacheKey, final HttpCacheEntry entry) { this.cacheKey = cacheKey; this.entry = entry; } public String getCacheKey() { return cacheKey; } public HttpCacheEntry getEntry() { return entry; } @Override public String toString() { return cacheKey; } } WarningValue.java000066400000000000000000000304621434266521000371140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; /** This class provides for parsing and understanding Warning headers. As * the Warning header can be multi-valued, but the values can contain * separators like commas inside quoted strings, we cannot use the regular * {@link Header#getValue()} } call to access the values. */ class WarningValue { private int offs; private final int init_offs; private final String src; private int warnCode; private String warnAgent; private String warnText; private Instant warnDate; WarningValue(final String s) { this(s, 0); } WarningValue(final String s, final int offs) { this.offs = this.init_offs = offs; this.src = s; consumeWarnValue(); } /** Returns an array of the parsable warning values contained * in the given header value, which is assumed to be a * Warning header. Improperly formatted warning values will be * skipped, in keeping with the philosophy of "ignore what you * cannot understand." * @param h Warning {@link Header} to parse * @return array of {@code WarnValue} objects */ public static WarningValue[] getWarningValues(final Header h) { final List out = new ArrayList<>(); final String src = h.getValue(); int offs = 0; while(offs < src.length()) { try { final WarningValue wv = new WarningValue(src, offs); out.add(wv); offs = wv.offs; } catch (final IllegalArgumentException e) { final int nextComma = src.indexOf(',', offs); if (nextComma == -1) { break; } offs = nextComma + 1; } } final WarningValue[] wvs = {}; return out.toArray(wvs); } /* * LWS = [CRLF] 1*( SP | HT ) * CRLF = CR LF */ protected void consumeLinearWhitespace() { while(offs < src.length()) { switch(src.charAt(offs)) { case '\r': if (offs+2 >= src.length() || src.charAt(offs+1) != '\n' || (src.charAt(offs+2) != ' ' && src.charAt(offs+2) != '\t')) { return; } offs += 2; break; case ' ': case '\t': break; default: return; } offs++; } } /* * CHAR = */ private boolean isChar(final char c) { return ((int) c >= 0 && (int) c <= 127); } /* * CTL = */ private boolean isControl(final char c) { return ((int) c == 127 || ((int) c >=0 && (int) c <= 31)); } /* * separators = "(" | ")" | "<" | ">" | "@" * | "," | ";" | ":" | "\" | <"> * | "/" | "[" | "]" | "?" | "=" * | "{" | "}" | SP | HT */ private boolean isSeparator(final char c) { return (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || c == ',' || c == ';' || c == ':' || c == '\\' || c == '\"' || c == '/' || c == '[' || c == ']' || c == '?' || c == '=' || c == '{' || c == '}' || c == ' ' || c == '\t'); } /* * token = 1* */ protected void consumeToken() { if (!isTokenChar(src.charAt(offs))) { parseError(); } while(offs < src.length()) { if (!isTokenChar(src.charAt(offs))) { break; } offs++; } } private boolean isTokenChar(final char c) { return (isChar(c) && !isControl(c) && !isSeparator(c)); } private static final String TOPLABEL = "\\p{Alpha}([\\p{Alnum}-]*\\p{Alnum})?"; private static final String DOMAINLABEL = "\\p{Alnum}([\\p{Alnum}-]*\\p{Alnum})?"; private static final String HOSTNAME = "(" + DOMAINLABEL + "\\.)*" + TOPLABEL + "\\.?"; private static final String IPV4ADDRESS = "\\d+\\.\\d+\\.\\d+\\.\\d+"; private static final String HOST = "(" + HOSTNAME + ")|(" + IPV4ADDRESS + ")"; private static final String PORT = "\\d*"; private static final String HOSTPORT = "(" + HOST + ")(\\:" + PORT + ")?"; private static final Pattern HOSTPORT_PATTERN = Pattern.compile(HOSTPORT); protected void consumeHostPort() { final Matcher m = HOSTPORT_PATTERN.matcher(src.substring(offs)); if (!m.find()) { parseError(); } if (m.start() != 0) { parseError(); } offs += m.end(); } /* * warn-agent = ( host [ ":" port ] ) | pseudonym * pseudonym = token */ protected void consumeWarnAgent() { final int curr_offs = offs; try { consumeHostPort(); warnAgent = src.substring(curr_offs, offs); consumeCharacter(' '); return; } catch (final IllegalArgumentException e) { offs = curr_offs; } consumeToken(); warnAgent = src.substring(curr_offs, offs); consumeCharacter(' '); } /* * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) * qdtext = > */ protected void consumeQuotedString() { if (src.charAt(offs) != '\"') { parseError(); } offs++; boolean foundEnd = false; while(offs < src.length() && !foundEnd) { final char c = src.charAt(offs); if (offs + 1 < src.length() && c == '\\' && isChar(src.charAt(offs+1))) { offs += 2; // consume quoted-pair } else if (c == '\"') { foundEnd = true; offs++; } else if (!isControl(c)) { offs++; } else { parseError(); } } if (!foundEnd) { parseError(); } } /* * warn-text = quoted-string */ protected void consumeWarnText() { final int curr = offs; consumeQuotedString(); warnText = src.substring(curr, offs); } private static final String MONTH = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec"; private static final String WEEKDAY = "Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday"; private static final String WKDAY = "Mon|Tue|Wed|Thu|Fri|Sat|Sun"; private static final String TIME = "\\d{2}:\\d{2}:\\d{2}"; private static final String DATE3 = "(" + MONTH + ") ( |\\d)\\d"; private static final String DATE2 = "\\d{2}-(" + MONTH + ")-\\d{2}"; private static final String DATE1 = "\\d{2} (" + MONTH + ") \\d{4}"; private static final String ASCTIME_DATE = "(" + WKDAY + ") (" + DATE3 + ") (" + TIME + ") \\d{4}"; private static final String RFC850_DATE = "(" + WEEKDAY + "), (" + DATE2 + ") (" + TIME + ") GMT"; private static final String RFC1123_DATE = "(" + WKDAY + "), (" + DATE1 + ") (" + TIME + ") GMT"; private static final String HTTP_DATE = "(" + RFC1123_DATE + ")|(" + RFC850_DATE + ")|(" + ASCTIME_DATE + ")"; private static final String WARN_DATE = "\"(" + HTTP_DATE + ")\""; private static final Pattern WARN_DATE_PATTERN = Pattern.compile(WARN_DATE); /* * warn-date = <"> HTTP-date <"> */ protected void consumeWarnDate() { final int curr = offs; final Matcher m = WARN_DATE_PATTERN.matcher(src.substring(offs)); if (!m.lookingAt()) { parseError(); } offs += m.end(); warnDate = DateUtils.parseStandardDate(src.substring(curr+1,offs-1)); } /* * warning-value = warn-code SP warn-agent SP warn-text [SP warn-date] */ protected void consumeWarnValue() { consumeLinearWhitespace(); consumeWarnCode(); consumeWarnAgent(); consumeWarnText(); if (offs + 1 < src.length() && src.charAt(offs) == ' ' && src.charAt(offs+1) == '\"') { consumeCharacter(' '); consumeWarnDate(); } consumeLinearWhitespace(); if (offs != src.length()) { consumeCharacter(','); } } protected void consumeCharacter(final char c) { if (offs + 1 > src.length() || c != src.charAt(offs)) { parseError(); } offs++; } /* * warn-code = 3DIGIT */ protected void consumeWarnCode() { if (offs + 4 > src.length() || !Character.isDigit(src.charAt(offs)) || !Character.isDigit(src.charAt(offs + 1)) || !Character.isDigit(src.charAt(offs + 2)) || src.charAt(offs + 3) != ' ') { parseError(); } warnCode = Integer.parseInt(src.substring(offs,offs+3)); offs += 4; } private void parseError() { final String s = src.substring(init_offs); throw new IllegalArgumentException("Bad warn code \"" + s + "\""); } /** Returns the 3-digit code associated with this warning. * @return {@code int} */ public int getWarnCode() { return warnCode; } /** Returns the "warn-agent" string associated with this warning, * which is either the name or pseudonym of the server that added * this particular Warning header. * @return {@link String} */ public String getWarnAgent() { return warnAgent; } /** Returns the human-readable warning text for this warning. Note * that the original quoted-string is returned here, including * escaping for any contained characters. In other words, if the * header was: *
     *   Warning: 110 fred "Response is stale"
     * 
* then this method will return {@code "\"Response is stale\""} * (surrounding quotes included). * @return {@link String} */ public String getWarnText() { return warnText; } /** Returns the date and time when this warning was added, or * {@code null} if a warning date was not supplied in the * header. * @return {@link Instant} */ public Instant getWarnDate() { return warnDate; } /** Formats a {@code WarningValue} as a {@link String} * suitable for including in a header. For example, you can: *
     *   WarningValue wv = ...;
     *   HttpResponse resp = ...;
     *   resp.addHeader("Warning", wv.toString());
     * 
* @return {@link String} */ @Override public String toString() { if (warnDate != null) { return String.format("%d %s %s \"%s\"", warnCode, warnAgent, warnText, DateUtils.formatStandardDate(warnDate)); } else { return String.format("%d %s %s", warnCode, warnAgent, warnText); } } } ehcache/000077500000000000000000000000001434266521000352225ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cacheEhcacheHttpCacheStorage.java000066400000000000000000000134311434266521000425200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ehcache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.ehcache; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.impl.cache.AbstractSerializingCacheStorage; import org.apache.hc.client5.http.impl.cache.ByteArrayCacheEntrySerializer; import org.apache.hc.client5.http.impl.cache.CacheConfig; import org.apache.hc.client5.http.impl.cache.NoopCacheEntrySerializer; import org.apache.hc.core5.util.Args; import org.ehcache.Cache; /** *

This class is a storage backend for cache entries that uses the * popular Ehcache cache implementation. * In particular, this backend allows for spillover to disk, where the * cache can be effectively larger than memory, and cached responses are * paged into and out of memory from disk as needed.

* *

N.B. Since the Ehcache is configured ahead of time with a * maximum number of cache entries, this effectively ignores the * {@link CacheConfig#getMaxCacheEntries()} maximum cache entries} * specified by a provided {@link CacheConfig}.

* *

Please refer to the * Ehcache documentation for details on how to configure the Ehcache * itself.

* @since 4.1 */ public class EhcacheHttpCacheStorage extends AbstractSerializingCacheStorage { /** * Creates cache that stores {@link HttpCacheStorageEntry}s without direct serialization. * * @since 5.0 */ public static EhcacheHttpCacheStorage createObjectCache( final Cache cache, final CacheConfig config) { return new EhcacheHttpCacheStorage<>(cache, config, NoopCacheEntrySerializer.INSTANCE); } /** * Creates cache that stores serialized {@link HttpCacheStorageEntry}s. * * @since 5.0 */ public static EhcacheHttpCacheStorage createSerializedCache( final Cache cache, final CacheConfig config) { return new EhcacheHttpCacheStorage<>(cache, config, ByteArrayCacheEntrySerializer.INSTANCE); } private final Cache cache; /** * Constructs a storage backend using the provided Ehcache * with the given configuration options, but using an alternative * cache entry serialization strategy. * @param cache where to store cached origin responses * @param config cache storage configuration options - note that * the setting for max object size will be ignored and * should be configured in the Ehcache instead. * @param serializer alternative serialization mechanism */ public EhcacheHttpCacheStorage( final Cache cache, final CacheConfig config, final HttpCacheEntrySerializer serializer) { super((config != null ? config : CacheConfig.DEFAULT).getMaxUpdateRetries(), serializer); this.cache = Args.notNull(cache, "Ehcache"); } @Override protected String digestToStorageKey(final String key) { return key; } @Override protected void store(final String storageKey, final T storageObject) throws ResourceIOException { cache.put(storageKey, storageObject); } @Override protected T restore(final String storageKey) throws ResourceIOException { return cache.get(storageKey); } @Override protected T getForUpdateCAS(final String storageKey) throws ResourceIOException { return cache.get(storageKey); } @Override protected T getStorageObject(final T element) throws ResourceIOException { return element; } @Override protected boolean updateCAS( final String storageKey, final T oldStorageObject, final T storageObject) throws ResourceIOException { return cache.replace(storageKey, oldStorageObject, storageObject); } @Override protected void delete(final String storageKey) throws ResourceIOException { cache.remove(storageKey); } @Override protected Map bulkRestore(final Collection storageKeys) throws ResourceIOException { final Map resultMap = new HashMap<>(); for (final String storageKey: storageKeys) { final T storageObject = cache.get(storageKey); if (storageObject != null) { resultMap.put(storageKey, storageObject); } } return resultMap; } } package-info.java000066400000000000000000000024631434266521000404160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ehcache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Ehcache based caching backend implementation. */ package org.apache.hc.client5.http.impl.cache.ehcache; memcached/000077500000000000000000000000001434266521000355505ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cacheKeyHashingScheme.java000066400000000000000000000063651434266521000416040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */package org.apache.hc.client5.http.impl.cache.memcached; /** * Since the {@link org.apache.hc.client5.http.cache.HttpCacheStorage} interface * expects to use variant-annotated URLs for its storage keys, but Memcached * has a maximum key size, we need to support mapping storage keys to cache * keys. Clients can implement this interface to change the way the mapping * is done (for example, to add a prefix to all cache keys to provide a form * of memcached namespacing). * * @since 4.1 */ public interface KeyHashingScheme { /** Maps a storage key to a cache key. The storage key is what * the higher-level HTTP cache uses as a key; the cache key is what * we use as a key for talking to memcached. * @param storageKey what the higher-level HTTP cache wants to use * as its key for looking up cache entries * @return a cache key suitable for use with memcached */ String hash(String storageKey); } MemcachedHttpAsyncCacheStorage.java000066400000000000000000000235011434266521000443710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.client5.http.impl.cache.AbstractBinaryAsyncCacheStorage; import org.apache.hc.client5.http.impl.cache.ByteArrayCacheEntrySerializer; import org.apache.hc.client5.http.impl.cache.CacheConfig; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.util.Args; import net.spy.memcached.CASResponse; import net.spy.memcached.CASValue; import net.spy.memcached.MemcachedClient; import net.spy.memcached.internal.BulkFuture; import net.spy.memcached.internal.GetFuture; import net.spy.memcached.internal.OperationFuture; /** *

* This class is a storage backend that uses an external memcached * for storing cached origin responses. This storage option provides a * couple of interesting advantages over the default in-memory storage * backend: *

*
    *
  1. in-memory cached objects can survive an application restart since * they are held in a separate process
  2. *
  3. it becomes possible for several cooperating applications to share * a large memcached farm together
  4. *
*

* Note that in a shared memcached pool setting you may wish to make use * of the Ketama consistent hashing algorithm to reduce the number of * cache misses that might result if one of the memcached cluster members * fails (see the * KetamaConnectionFactory). *

*

* Because memcached places limits on the size of its keys, we need to * introduce a key hashing scheme to map the annotated URLs the higher-level * caching HTTP client wants to use as keys onto ones that are suitable * for use with memcached. Please see {@link KeyHashingScheme} if you would * like to use something other than the provided {@link SHA256KeyHashingScheme}. *

* *

* Please refer to the * memcached documentation and in particular to the documentation for * the spymemcached * documentation for details about how to set up and configure memcached * and the Java client used here, respectively. *

* * @since 5.0 */ public class MemcachedHttpAsyncCacheStorage extends AbstractBinaryAsyncCacheStorage> { private final MemcachedClient client; private final KeyHashingScheme keyHashingScheme; /** * Create a storage backend talking to a memcached instance * listening on the specified host and port. This is useful if you * just have a single local memcached instance running on the same * machine as your application, for example. * @param address where the memcached daemon is running * @throws IOException in case of an error */ public MemcachedHttpAsyncCacheStorage(final InetSocketAddress address) throws IOException { this(new MemcachedClient(address)); } /** * Create a storage backend using the pre-configured given * memcached client. * @param cache client to use for communicating with memcached */ public MemcachedHttpAsyncCacheStorage(final MemcachedClient cache) { this(cache, CacheConfig.DEFAULT, ByteArrayCacheEntrySerializer.INSTANCE, SHA256KeyHashingScheme.INSTANCE); } /** * Create a storage backend using the given memcached client and * applying the given cache configuration, serialization, and hashing * mechanisms. * @param client how to talk to memcached * @param config apply HTTP cache-related options * @param serializer alternative serialization mechanism * @param keyHashingScheme how to map higher-level logical "storage keys" * onto "cache keys" suitable for use with memcached */ public MemcachedHttpAsyncCacheStorage( final MemcachedClient client, final CacheConfig config, final HttpCacheEntrySerializer serializer, final KeyHashingScheme keyHashingScheme) { super((config != null ? config : CacheConfig.DEFAULT).getMaxUpdateRetries(), serializer != null ? serializer : ByteArrayCacheEntrySerializer.INSTANCE); this.client = Args.notNull(client, "Memcached client"); this.keyHashingScheme = keyHashingScheme; } @Override protected String digestToStorageKey(final String key) { return keyHashingScheme.hash(key); } private byte[] castAsByteArray(final Object storageObject) throws ResourceIOException { if (storageObject == null) { return null; } if (storageObject instanceof byte[]) { return (byte[]) storageObject; } throw new ResourceIOException("Unexpected cache content: " + storageObject.getClass()); } @Override protected byte[] getStorageObject(final CASValue casValue) throws ResourceIOException { return castAsByteArray(casValue.getValue()); } private Cancellable operation(final OperationFuture operationFuture, final FutureCallback callback) { operationFuture.addListener(future -> { try { callback.completed(operationFuture.get()); } catch (final ExecutionException ex) { if (ex.getCause() instanceof Exception) { callback.failed((Exception) ex.getCause()); } else { callback.failed(ex); } } }); return Operations.cancellable(operationFuture); } @Override protected Cancellable store(final String storageKey, final byte[] storageObject, final FutureCallback callback) { return operation(client.set(storageKey, 0, storageObject), callback); } @Override protected Cancellable restore(final String storageKey, final FutureCallback callback) { final GetFuture getFuture = client.asyncGet(storageKey); getFuture.addListener(future -> { try { callback.completed(castAsByteArray(getFuture.get())); } catch (final ExecutionException ex) { if (ex.getCause() instanceof Exception) { callback.failed((Exception) ex.getCause()); } else { callback.failed(ex); } } }); return Operations.cancellable(getFuture); } @Override protected Cancellable getForUpdateCAS(final String storageKey, final FutureCallback> callback) { return operation(client.asyncGets(storageKey), callback); } @Override protected Cancellable updateCAS( final String storageKey, final CASValue casValue, final byte[] storageObject, final FutureCallback callback) { return operation(client.asyncCAS(storageKey, casValue.getCas(), storageObject), new FutureCallback() { @Override public void completed(final CASResponse result) { callback.completed(result == CASResponse.OK); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); } @Override protected Cancellable delete(final String storageKey, final FutureCallback callback) { return operation(client.delete(storageKey), callback); } @Override protected Cancellable bulkRestore(final Collection storageKeys, final FutureCallback> callback) { final BulkFuture> future = client.asyncGetBulk(storageKeys); future.addListener(future1 -> { final Map storageObjectMap = future1.get(); final Map resultMap = new HashMap<>(storageObjectMap.size()); for (final Map.Entry resultEntry: storageObjectMap.entrySet()) { resultMap.put(resultEntry.getKey(), castAsByteArray(resultEntry.getValue())); } callback.completed(resultMap); }); return Operations.cancellable(future); } } MemcachedHttpCacheStorage.java000066400000000000000000000224221434266521000433740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.client5.http.impl.cache.AbstractBinaryCacheStorage; import org.apache.hc.client5.http.impl.cache.ByteArrayCacheEntrySerializer; import org.apache.hc.client5.http.impl.cache.CacheConfig; import org.apache.hc.core5.util.Args; import net.spy.memcached.CASResponse; import net.spy.memcached.CASValue; import net.spy.memcached.MemcachedClient; import net.spy.memcached.MemcachedClientIF; import net.spy.memcached.OperationTimeoutException; /** *

* This class is a storage backend that uses an external memcached * for storing cached origin responses. This storage option provides a * couple of interesting advantages over the default in-memory storage * backend: *

*
    *
  1. in-memory cached objects can survive an application restart since * they are held in a separate process
  2. *
  3. it becomes possible for several cooperating applications to share * a large memcached farm together
  4. *
*

* Note that in a shared memcached pool setting you may wish to make use * of the Ketama consistent hashing algorithm to reduce the number of * cache misses that might result if one of the memcached cluster members * fails (see the * KetamaConnectionFactory). *

*

* Because memcached places limits on the size of its keys, we need to * introduce a key hashing scheme to map the annotated URLs the higher-level * caching HTTP client wants to use as keys onto ones that are suitable * for use with memcached. Please see {@link KeyHashingScheme} if you would * like to use something other than the provided {@link SHA256KeyHashingScheme}. *

* *

* Please refer to the * memcached documentation and in particular to the documentation for * the spymemcached * documentation for details about how to set up and configure memcached * and the Java client used here, respectively. *

* * @since 4.1 */ public class MemcachedHttpCacheStorage extends AbstractBinaryCacheStorage> { private final MemcachedClientIF client; private final KeyHashingScheme keyHashingScheme; /** * Create a storage backend talking to a memcached instance * listening on the specified host and port. This is useful if you * just have a single local memcached instance running on the same * machine as your application, for example. * @param address where the memcached daemon is running * @throws IOException in case of an error */ public MemcachedHttpCacheStorage(final InetSocketAddress address) throws IOException { this(new MemcachedClient(address)); } /** * Create a storage backend using the pre-configured given * memcached client. * @param cache client to use for communicating with memcached */ public MemcachedHttpCacheStorage(final MemcachedClient cache) { this(cache, CacheConfig.DEFAULT, ByteArrayCacheEntrySerializer.INSTANCE, SHA256KeyHashingScheme.INSTANCE); } /** * Create a storage backend using the pre-configured given * memcached client. * * @param cache client to use for communicating with memcached * * @since 5.2 */ public MemcachedHttpCacheStorage(final MemcachedClientIF cache) { this(cache, CacheConfig.DEFAULT, ByteArrayCacheEntrySerializer.INSTANCE, SHA256KeyHashingScheme.INSTANCE); } /** * Create a storage backend using the given memcached client and * applying the given cache configuration, serialization, and hashing * mechanisms. * @param client how to talk to memcached * @param config apply HTTP cache-related options * @param serializer alternative serialization mechanism * @param keyHashingScheme how to map higher-level logical "storage keys" * onto "cache keys" suitable for use with memcached */ public MemcachedHttpCacheStorage( final MemcachedClient client, final CacheConfig config, final HttpCacheEntrySerializer serializer, final KeyHashingScheme keyHashingScheme) { this((MemcachedClientIF) client, config, serializer, keyHashingScheme); } /** * Create a storage backend using the given memcached client and * applying the given cache configuration, serialization, and hashing * mechanisms. * * @param client how to talk to memcached * @param config apply HTTP cache-related options * @param serializer alternative serialization mechanism * @param keyHashingScheme how to map higher-level logical "storage keys" * onto "cache keys" suitable for use with memcached * @since 5.2 */ public MemcachedHttpCacheStorage( final MemcachedClientIF client, final CacheConfig config, final HttpCacheEntrySerializer serializer, final KeyHashingScheme keyHashingScheme) { super((config != null ? config : CacheConfig.DEFAULT).getMaxUpdateRetries(), serializer != null ? serializer : ByteArrayCacheEntrySerializer.INSTANCE); this.client = Args.notNull(client, "Memcached client"); this.keyHashingScheme = keyHashingScheme; } @Override protected String digestToStorageKey(final String key) { return keyHashingScheme.hash(key); } @Override protected void store(final String storageKey, final byte[] storageObject) throws ResourceIOException { client.set(storageKey, 0, storageObject); } private byte[] castAsByteArray(final Object storageObject) throws ResourceIOException { if (storageObject == null) { return null; } if (storageObject instanceof byte[]) { return (byte[]) storageObject; } throw new ResourceIOException("Unexpected cache content: " + storageObject.getClass()); } @Override protected byte[] restore(final String storageKey) throws ResourceIOException { try { return castAsByteArray(client.get(storageKey)); } catch (final OperationTimeoutException ex) { throw new MemcachedOperationTimeoutException(ex); } } @Override protected CASValue getForUpdateCAS(final String storageKey) throws ResourceIOException { try { return client.gets(storageKey); } catch (final OperationTimeoutException ex) { throw new MemcachedOperationTimeoutException(ex); } } @Override protected byte[] getStorageObject(final CASValue casValue) throws ResourceIOException { return castAsByteArray(casValue.getValue()); } @Override protected boolean updateCAS( final String storageKey, final CASValue casValue, final byte[] storageObject) throws ResourceIOException { final CASResponse casResult = client.cas(storageKey, casValue.getCas(), storageObject); return casResult == CASResponse.OK; } @Override protected void delete(final String storageKey) throws ResourceIOException { client.delete(storageKey); } @Override protected Map bulkRestore(final Collection storageKeys) throws ResourceIOException { final Map storageObjectMap = client.getBulk(storageKeys); final Map resultMap = new HashMap<>(storageObjectMap.size()); for (final Map.Entry resultEntry: storageObjectMap.entrySet()) { resultMap.put(resultEntry.getKey(), castAsByteArray(resultEntry.getValue())); } return resultMap; } } MemcachedKeyHashingException.java000066400000000000000000000031451434266521000441160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; /** * Signals a problem encountered when trying to map a * logical "storage key" to a "cache key" suitable for use with * memcached. * * @since 4.1 */ public class MemcachedKeyHashingException extends RuntimeException { private static final long serialVersionUID = -7553380015989141114L; public MemcachedKeyHashingException(final Throwable cause) { super(cause); } } MemcachedOperationTimeoutException.java000066400000000000000000000031571434266521000453760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; import org.apache.hc.client5.http.cache.ResourceIOException; /** * Signals memcached operation timeout. * * @since 4.1 */ public class MemcachedOperationTimeoutException extends ResourceIOException { private static final long serialVersionUID = 1L; public MemcachedOperationTimeoutException(final Throwable cause) { super(cause != null ? cause.getMessage() : null, cause); } } PrefixKeyHashingScheme.java000066400000000000000000000044651434266521000427610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; /** * This is a {@link KeyHashingScheme} decorator that simply adds * a known prefix to the results of another {@code KeyHashingScheme}. * Primarily useful for namespacing a shared memcached cluster, for * example. * * @since 4.1 */ public final class PrefixKeyHashingScheme implements KeyHashingScheme { private final String prefix; private final KeyHashingScheme backingScheme; /** * Creates a new {@link KeyHashingScheme} that prepends the given * prefix to the results of hashes from the given backing scheme. * Users should be aware that memcached has a fixed maximum key * length, so the combination of this prefix plus the results of * the backing hashing scheme must still fit within these limits. * @param prefix * @param backingScheme */ public PrefixKeyHashingScheme(final String prefix, final KeyHashingScheme backingScheme) { this.prefix = prefix; this.backingScheme = backingScheme; } @Override public String hash(final String storageKey) { return prefix + backingScheme.hash(storageKey); } } SHA256KeyHashingScheme.java000066400000000000000000000046431434266521000423720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.hc.client5.http.utils.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is a {@link KeyHashingScheme} based on the * SHA-256 * algorithm. The hashes produced are hex-encoded SHA-256 * digests and hence are always 64-character hexadecimal * strings. * * @since 4.1 */ public final class SHA256KeyHashingScheme implements KeyHashingScheme { public static final SHA256KeyHashingScheme INSTANCE = new SHA256KeyHashingScheme(); private static final Logger LOG = LoggerFactory.getLogger(SHA256KeyHashingScheme.class); @Override public String hash(final String key) { final MessageDigest md = getDigest(); md.update(key.getBytes()); return Hex.encodeHexString(md.digest()); } private MessageDigest getDigest() { try { return MessageDigest.getInstance("SHA-256"); } catch (final NoSuchAlgorithmException nsae) { LOG.error("can't find SHA-256 implementation for cache key hashing"); throw new MemcachedKeyHashingException(nsae); } } } package-info.java000066400000000000000000000024741434266521000407460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Memcached based caching backend * implementation. */ package org.apache.hc.client5.http.impl.cache.memcached; package-info.java000066400000000000000000000024641434266521000370370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Caching API implementation for both the classic and * the asynchronous HTTP transports. */ package org.apache.hc.client5.http.impl.cache; schedule/000077500000000000000000000000001434266521000343735ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/implExponentialBackOffSchedulingStrategy.java000066400000000000000000000110231434266521000444660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.schedule; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** * An implementation that backs off exponentially based on the number of * consecutive failed attempts. It uses the following defaults: *
 *      no delay in case it was never tried or didn't fail so far
 *     6 s delay for one failed attempt (= {@link #getInitialExpiry()})
 *    60 s delay for two failed attempts
 *  10 min delay for three failed attempts
 * 100 min delay for four failed attempts
 *   ~16 h delay for five failed attempts
 *    24 h delay for six or more failed attempts (= {@link #getMaxExpiry()})
 * 
* * The following equation is used to calculate the delay for a specific pending operation: *
 *     delay = {@link #getInitialExpiry()} * Math.pow({@link #getBackOffRate()},
 *     {@code consecutiveFailedAttempts} - 1))
 * 
* The resulting delay won't exceed {@link #getMaxExpiry()}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class ExponentialBackOffSchedulingStrategy implements SchedulingStrategy { public static final long DEFAULT_BACK_OFF_RATE = 10; public static final TimeValue DEFAULT_INITIAL_EXPIRY = TimeValue.ofSeconds(6); public static final TimeValue DEFAULT_MAX_EXPIRY = TimeValue.ofSeconds(86400); private static final ExponentialBackOffSchedulingStrategy INSTANCE = new ExponentialBackOffSchedulingStrategy( DEFAULT_BACK_OFF_RATE, DEFAULT_INITIAL_EXPIRY, DEFAULT_MAX_EXPIRY); private final long backOffRate; private final TimeValue initialExpiry; private final TimeValue maxExpiry; public ExponentialBackOffSchedulingStrategy( final long backOffRate, final TimeValue initialExpiry, final TimeValue maxExpiry) { this.backOffRate = Args.notNegative(backOffRate, "BackOff rate"); this.initialExpiry = Args.notNull(initialExpiry, "Initial expiry"); this.maxExpiry = Args.notNull(maxExpiry, "Max expiry"); } public ExponentialBackOffSchedulingStrategy(final long backOffRate, final TimeValue initialExpiry) { this(backOffRate, initialExpiry, DEFAULT_MAX_EXPIRY); } public ExponentialBackOffSchedulingStrategy(final long backOffRate) { this(backOffRate, DEFAULT_INITIAL_EXPIRY, DEFAULT_MAX_EXPIRY); } public ExponentialBackOffSchedulingStrategy() { this(DEFAULT_BACK_OFF_RATE, DEFAULT_INITIAL_EXPIRY, DEFAULT_MAX_EXPIRY); } @Override public TimeValue schedule(final int attemptNumber) { return calculateDelay(attemptNumber); } public long getBackOffRate() { return backOffRate; } public TimeValue getInitialExpiry() { return initialExpiry; } public TimeValue getMaxExpiry() { return maxExpiry; } protected TimeValue calculateDelay(final int consecutiveFailedAttempts) { if (consecutiveFailedAttempts > 0) { final long delay = (long) (initialExpiry.toMilliseconds() * Math.pow(backOffRate, consecutiveFailedAttempts - 1)); return TimeValue.ofMilliseconds(Math.min(delay, maxExpiry.toMilliseconds())); } return TimeValue.ZERO_MILLISECONDS; } } ImmediateSchedulingStrategy.java000066400000000000000000000035041434266521000426670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.schedule; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.TimeValue; /** * Immediately schedules any operation. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class ImmediateSchedulingStrategy implements SchedulingStrategy { public final static ImmediateSchedulingStrategy INSTANCE = new ImmediateSchedulingStrategy(); @Override public TimeValue schedule(final int attemptNumber) { return TimeValue.ZERO_MILLISECONDS; } } package-info.java000066400000000000000000000024131434266521000375620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Commons scheduling strategy implementations. */ package org.apache.hc.client5.http.impl.schedule; schedule/000077500000000000000000000000001434266521000334325ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/httpConcurrentCountMap.java000066400000000000000000000053261434266521000400740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.schedule; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Concurrent map of integer counts. * * @since 5.0 * * @param identifier used as a map key */ @Contract(threading = ThreadingBehavior.SAFE) public final class ConcurrentCountMap { private final ConcurrentMap map; public ConcurrentCountMap() { this.map = new ConcurrentHashMap<>(); } public int getCount(final T identifier) { Args.notNull(identifier, "Identifier"); final AtomicInteger count = map.get(identifier); return count != null ? count.get() : 0; } public void resetCount(final T identifier) { Args.notNull(identifier, "Identifier"); map.remove(identifier); } public int increaseCount(final T identifier) { Args.notNull(identifier, "Identifier"); final AtomicInteger count = get(identifier); return count.incrementAndGet(); } private AtomicInteger get(final T identifier) { AtomicInteger entry = map.get(identifier); if (entry == null) { final AtomicInteger newEntry = new AtomicInteger(); entry = map.putIfAbsent(identifier, newEntry); if (entry == null) { entry = newEntry; } } return entry; } } SchedulingStrategy.java000066400000000000000000000032161434266521000401070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.schedule; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.TimeValue; /** * Strategy to determine an execution time (schedule) for an operation. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface SchedulingStrategy { /** * Schedules execution time for an operation. */ TimeValue schedule(int attemptNumber); } package-info.java000066400000000000000000000024231434266521000366220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/main/java/org/apache/hc/client5/http/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Scheduling strategies used by HTTP cache implementations. */ package org.apache.hc.client5.http.schedule; httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/000077500000000000000000000000001434266521000241035ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/000077500000000000000000000000001434266521000250245ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/000077500000000000000000000000001434266521000256135ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/000077500000000000000000000000001434266521000270345ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/000077500000000000000000000000001434266521000274265ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/000077500000000000000000000000001434266521000307715ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000317505ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/000077500000000000000000000000001434266521000330135ustar00rootroot00000000000000ManagedHttpCacheStorageTest.java000066400000000000000000000065541434266521000411160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.hc.client5.http.impl.cache.CacheConfig; import org.apache.hc.client5.http.impl.cache.HttpTestUtils; import org.apache.hc.client5.http.impl.cache.ManagedHttpCacheStorage; import org.apache.hc.core5.http.HttpStatus; import org.junit.jupiter.api.Test; class ManagedHttpCacheStorageTest { @Test void putEntry() throws ResourceIOException { final CacheConfig cacheConfig = getCacheConfig(); try (final ManagedHttpCacheStorage cacheStorage = new ManagedHttpCacheStorage(cacheConfig)) { final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); cacheStorage.putEntry(key, value); assertEquals(HttpStatus.SC_OK, cacheStorage.getEntry(key).getStatus()); } } @Test void isActive() throws ResourceIOException { final CacheConfig cacheConfig = getCacheConfig(); final ManagedHttpCacheStorage cacheStorage = new ManagedHttpCacheStorage(cacheConfig); final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); cacheStorage.putEntry(key, value); assertTrue(cacheStorage.isActive()); cacheStorage.close(); assertFalse(cacheStorage.isActive()); } @Test void cacheDisableThrowsIllegalStateException() { final CacheConfig cacheConfig = getCacheConfig(); final ManagedHttpCacheStorage cacheStorage = new ManagedHttpCacheStorage(cacheConfig); final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); cacheStorage.close(); assertFalse(cacheStorage.isActive()); assertThrows(IllegalStateException.class, () -> cacheStorage.putEntry(key, value)); } private CacheConfig getCacheConfig() { return CacheConfig.custom() .setSharedCache(true) .setMaxObjectSize(262144) //256kb .build(); } } TestHttpCacheEntry.java000066400000000000000000000254341434266521000373340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import java.time.Instant; import java.time.temporal.ChronoField; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHeader; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestHttpCacheEntry { private Instant now; private Instant elevenSecondsAgo; private Instant nineSecondsAgo; private Resource mockResource; private HttpCacheEntry entry; @BeforeEach public void setUp() { now = Instant.now(); elevenSecondsAgo = now.minusSeconds(11); nineSecondsAgo = now.minusSeconds(9); mockResource = mock(Resource.class); } private HttpCacheEntry makeEntry(final Header[] headers) { return new HttpCacheEntry(elevenSecondsAgo, nineSecondsAgo, HttpStatus.SC_OK, headers, mockResource); } @Test public void testGetHeadersReturnsCorrectHeaders() { final Header[] headers = { new BasicHeader("foo", "fooValue"), new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") }; entry = makeEntry(headers); assertEquals(2, entry.getHeaders("bar").length); } @Test public void testGetFirstHeaderReturnsCorrectHeader() { final Header[] headers = { new BasicHeader("foo", "fooValue"), new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") }; entry = makeEntry(headers); assertEquals("barValue1", entry.getFirstHeader("bar").getValue()); } @Test public void testGetHeadersReturnsEmptyArrayIfNoneMatch() { final Header[] headers = { new BasicHeader("foo", "fooValue"), new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") }; entry = makeEntry(headers); assertEquals(0, entry.getHeaders("baz").length); } @Test public void testGetFirstHeaderReturnsNullIfNoneMatch() { final Header[] headers = { new BasicHeader("foo", "fooValue"), new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") }; entry = makeEntry(headers); assertNull(entry.getFirstHeader("quux")); } @Test public void testCacheEntryWithOneVaryHeaderHasVariants() { final Header[] headers = { new BasicHeader("Vary", "User-Agent") }; entry = makeEntry(headers); assertTrue(entry.hasVariants()); } @Test public void testCacheEntryWithMultipleVaryHeadersHasVariants() { final Header[] headers = { new BasicHeader("Vary", "User-Agent"), new BasicHeader("Vary", "Accept-Encoding") }; entry = makeEntry(headers); assertTrue(entry.hasVariants()); } @Test public void testCacheEntryWithVaryStarHasVariants(){ final Header[] headers = { new BasicHeader("Vary", "*") }; entry = makeEntry(headers); assertTrue(entry.hasVariants()); } @Test public void testGetMethodReturnsCorrectRequestMethod() { final Header[] headers = { new BasicHeader("foo", "fooValue"), new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") }; entry = makeEntry(headers); assertEquals(HeaderConstants.GET_METHOD, entry.getRequestMethod()); } @SuppressWarnings("unused") @Test public void mustProvideRequestDate() { try { new HttpCacheEntry(null, Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource); fail("Should have thrown exception"); } catch (final NullPointerException expected) { } } @SuppressWarnings("unused") @Test public void mustProvideResponseDate() { try { new HttpCacheEntry(Instant.now(), null, HttpStatus.SC_OK, new Header[]{}, mockResource); fail("Should have thrown exception"); } catch (final NullPointerException expected) { } } @SuppressWarnings("unused") @Test public void mustProvideResponseHeaders() { try { new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, null, mockResource); fail("Should have thrown exception"); } catch (final NullPointerException expected) { } } @Test public void statusCodeComesFromOriginalStatusLine() { entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource); assertEquals(HttpStatus.SC_OK, entry.getStatus()); } @Test public void canGetOriginalRequestDate() { final Instant requestDate = Instant.now(); entry = new HttpCacheEntry(requestDate, Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource); assertEquals(requestDate, entry.getRequestInstant()); } @Test public void canGetOriginalResponseDate() { final Instant responseDate = Instant.now(); entry = new HttpCacheEntry(Instant.now(), responseDate, HttpStatus.SC_OK, new Header[]{}, mockResource); assertEquals(responseDate, entry.getResponseInstant()); } @Test public void canGetOriginalResource() { entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource); assertSame(mockResource, entry.getResource()); } @Test public void canGetOriginalHeaders() { final Header[] headers = { new BasicHeader("Server", "MockServer/1.0"), new BasicHeader("Date", DateUtils.formatStandardDate(now)) }; entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource); final Header[] result = entry.getHeaders(); assertEquals(headers.length, result.length); for(int i=0; i()); } @Test public void canRetrieveOriginalVariantMap() { final Map variantMap = new HashMap<>(); variantMap.put("A","B"); variantMap.put("C","D"); entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource, variantMap); final Map result = entry.getVariantMap(); assertEquals(2, result.size()); assertEquals("B", result.get("A")); assertEquals("D", result.get("C")); } @Test public void retrievedVariantMapIsNotModifiable() { final Map variantMap = new HashMap<>(); variantMap.put("A","B"); variantMap.put("C","D"); entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource, variantMap); final Map result = entry.getVariantMap(); try { result.remove("A"); fail("Should have thrown exception"); } catch (final UnsupportedOperationException expected) { } try { result.put("E","F"); fail("Should have thrown exception"); } catch (final UnsupportedOperationException expected) { } } @Test public void canConvertToString() { entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource); assertNotNull(entry.toString()); assertNotEquals("", entry.toString()); } @Test public void testMissingDateHeaderIsIgnored() { final Header[] headers = new Header[] {}; entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource); assertNull(entry.getDate()); } @Test public void testMalformedDateHeaderIsIgnored() { final Header[] headers = new Header[] { new BasicHeader("Date", "asdf") }; entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource); assertNull(entry.getDate()); } @Test public void testValidDateHeaderIsParsed() { final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0); final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(date)) }; entry = new HttpCacheEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource); final Date dateHeaderValue = entry.getDate(); assertNotNull(dateHeaderValue); assertEquals(DateUtils.toDate(date), dateHeaderValue); } } httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/000077500000000000000000000000001434266521000327115ustar00rootroot00000000000000cache/000077500000000000000000000000001434266521000336755ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/implConsumableInputStream.java000066400000000000000000000034751434266521000410350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.io.Closer; public class ConsumableInputStream extends InputStream { private final ByteArrayInputStream buf; private boolean closed; public ConsumableInputStream(final ByteArrayInputStream buf) { this.buf = buf; } @Override public int read() throws IOException { return buf.read(); } @Override public void close() { closed = true; Closer.closeQuietly(buf); } public boolean wasClosed() { return closed; } } ContainsHeaderMatcher.java000066400000000000000000000053301434266521000407340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.Iterator; import java.util.Objects; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MessageHeaders; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class ContainsHeaderMatcher extends BaseMatcher { private final String headerName; private final Object headerValue; public ContainsHeaderMatcher(final String headerName, final Object headerValue) { this.headerName = headerName; this.headerValue = headerValue; } @Override public boolean matches(final Object item) { if (item instanceof MessageHeaders) { final MessageHeaders messageHeaders = (MessageHeaders) item; for (final Iterator
it = messageHeaders.headerIterator(); it.hasNext(); ) { final Header header = it.next(); if (headerName.equalsIgnoreCase(header.getName()) && Objects.equals(headerValue, header.getValue())) { return true; } } } return false; } @Override public void describeTo(final Description description) { description.appendText("contains header ").appendValue(headerValue).appendText(": ").appendValue(headerValue); } public static Matcher contains(final String headerName, final Object headerValue) { return new ContainsHeaderMatcher(headerName, headerValue); } } HttpByteArrayCacheEntrySerializerTestUtils.java000066400000000000000000000343371434266521000451750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Collections; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; class HttpByteArrayCacheEntrySerializerTestUtils { private final static String TEST_RESOURCE_DIR = "src/test/resources/"; static final String TEST_STORAGE_KEY = "xyzzy"; /** * Template for incrementally building a new HttpCacheStorageEntry test object, starting from defaults. */ static class HttpCacheStorageEntryTestTemplate { Resource resource; Instant requestDate; Instant responseDate; int responseCode; Header[] responseHeaders; Map variantMap; String storageKey; /** * Return a new HttpCacheStorageEntryTestTemplate instance with all default values. * * @return new HttpCacheStorageEntryTestTemplate instance */ static HttpCacheStorageEntryTestTemplate makeDefault() { return new HttpCacheStorageEntryTestTemplate(DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE); } /** * Convert this template to a HttpCacheStorageEntry object. * @return HttpCacheStorageEntry object */ HttpCacheStorageEntry toEntry() { return new HttpCacheStorageEntry(storageKey, new HttpCacheEntry( requestDate, responseDate, responseCode, responseHeaders, resource, variantMap)); } /** * Create a new template with all null values. */ private HttpCacheStorageEntryTestTemplate() { } /** * Create a new template values copied from the given template * * @param src Template to copy values from */ private HttpCacheStorageEntryTestTemplate(final HttpCacheStorageEntryTestTemplate src) { this.resource = src.resource; this.requestDate = src.requestDate; this.responseDate = src.responseDate; this.responseCode = src.responseCode; this.responseHeaders = src.responseHeaders; this.variantMap = src.variantMap; this.storageKey = src.storageKey; } } /** * Template with all default values. * * Used by HttpCacheStorageEntryTestTemplate#makeDefault() */ private static final HttpCacheStorageEntryTestTemplate DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE = new HttpCacheStorageEntryTestTemplate(); static { DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.resource = new HeapResource("Hello World".getBytes(StandardCharsets.UTF_8)); DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.requestDate = Instant.ofEpochMilli(165214800000L); DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.responseDate = Instant.ofEpochMilli(2611108800000L); DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.responseCode = 200; DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.responseHeaders = new Header[]{ new BasicHeader("Content-type", "text/html"), new BasicHeader("Cache-control", "public, max-age=31536000"), }; DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.variantMap = Collections.emptyMap(); DEFAULT_HTTP_CACHE_STORAGE_ENTRY_TEST_TEMPLATE.storageKey = TEST_STORAGE_KEY; } /** * Test serializing and deserializing the given object with the given factory. *

* Compares fields to ensure the deserialized object is equivalent to the original object. * * @param serializer Factory for creating serializers * @param httpCacheStorageEntry Original object to serialize and test against * @throws Exception if anything goes wrong */ static void testWithCache(final HttpCacheEntrySerializer serializer, final HttpCacheStorageEntry httpCacheStorageEntry) throws Exception { final byte[] testBytes = serializer.serialize(httpCacheStorageEntry); verifyHttpCacheEntryFromBytes(serializer, httpCacheStorageEntry, testBytes); } /** * Verify that the given bytes deserialize to the given storage key and an equivalent cache entry. * * @param serializer Deserializer * @param httpCacheStorageEntry Cache entry to verify * @param testBytes Bytes to deserialize * @throws Exception if anything goes wrong */ static void verifyHttpCacheEntryFromBytes(final HttpCacheEntrySerializer serializer, final HttpCacheStorageEntry httpCacheStorageEntry, final byte[] testBytes) throws Exception { final HttpCacheStorageEntry testEntry = httpCacheStorageEntryFromBytes(serializer, testBytes); assertCacheEntriesEqual(httpCacheStorageEntry, testEntry); } /** * Verify that the given test file deserializes to a cache entry equivalent to the one given. * * @param serializer Deserializer * @param httpCacheStorageEntry Cache entry to verify * @param testFileName Name of test file to deserialize * @param reserializeFiles If true, test files will be regenerated and saved to disk * @throws Exception if anything goes wrong */ static void verifyHttpCacheEntryFromTestFile(final HttpCacheEntrySerializer serializer, final HttpCacheStorageEntry httpCacheStorageEntry, final String testFileName, final boolean reserializeFiles) throws Exception { if (reserializeFiles) { final File toFile = makeTestFileObject(testFileName); saveEntryToFile(serializer, httpCacheStorageEntry, toFile); } final byte[] bytes = readTestFileBytes(testFileName); verifyHttpCacheEntryFromBytes(serializer, httpCacheStorageEntry, bytes); } /** * Get the bytes of the given test file. * * @param testFileName Name of test file to get bytes from * @return Bytes from the given test file * @throws Exception if anything goes wrong */ static byte[] readTestFileBytes(final String testFileName) throws Exception { final File testFile = makeTestFileObject(testFileName); try(final FileInputStream testStream = new FileInputStream(testFile)) { return readFullyStrict(testStream, testFile.length()); } } /** * Create a new cache object from the given bytes. * * @param serializer Deserializer * @param testBytes Bytes to deserialize * @return Deserialized object */ static HttpCacheStorageEntry httpCacheStorageEntryFromBytes(final HttpCacheEntrySerializer serializer, final byte[] testBytes) throws ResourceIOException { return serializer.deserialize(testBytes); } /** * Assert that the given objects are equivalent * * @param expected Expected cache entry object * @param actual Actual cache entry object * @throws Exception if anything goes wrong */ static void assertCacheEntriesEqual(final HttpCacheStorageEntry expected, final HttpCacheStorageEntry actual) throws Exception { assertEquals(expected.getKey(), actual.getKey()); final HttpCacheEntry expectedContent = expected.getContent(); final HttpCacheEntry actualContent = actual.getContent(); assertEquals(expectedContent.getRequestInstant(), actualContent.getRequestInstant()); assertEquals(expectedContent.getResponseInstant(), actualContent.getResponseInstant()); assertEquals(expectedContent.getStatus(), actualContent.getStatus()); assertArrayEquals(expectedContent.getVariantMap().keySet().toArray(), actualContent.getVariantMap().keySet().toArray()); for (final String key : expectedContent.getVariantMap().keySet()) { assertEquals(expectedContent.getVariantMap().get(key), actualContent.getVariantMap().get(key), "Expected same variantMap values for key '" + key + "'"); } // Verify that the same headers are present on the expected and actual content. for(final Header expectedHeader: expectedContent.getHeaders()) { final Header actualHeader = actualContent.getFirstHeader(expectedHeader.getName()); if (actualHeader == null) { if (expectedHeader.getName().equalsIgnoreCase("content-length")) { // This header is added by the cache implementation, and can be safely ignored } else { fail("Expected header " + expectedHeader.getName() + " was not found"); } } else { assertEquals(expectedHeader.getName(), actualHeader.getName()); assertEquals(expectedHeader.getValue(), actualHeader.getValue()); } } if (expectedContent.getResource() == null) { assertNull(actualContent.getResource(), "Expected null resource"); } else { final byte[] expectedBytes = readFullyStrict( expectedContent.getResource().getInputStream(), (int) expectedContent.getResource().length() ); final byte[] actualBytes = readFullyStrict( actualContent.getResource().getInputStream(), (int) actualContent.getResource().length() ); assertArrayEquals(expectedBytes, actualBytes); } } /** * Get a File object for the given test file. * * @param testFileName Name of test file * @return File for this test file */ static File makeTestFileObject(final String testFileName) { return new File(TEST_RESOURCE_DIR + testFileName); } /** * Save the given cache entry serialized to the given file. * * @param serializer Serializer * @param httpCacheStorageEntry Cache entry to serialize and save * @param outFile Output file to write to * @throws Exception if anything goes wrong */ static void saveEntryToFile(final HttpCacheEntrySerializer serializer, final HttpCacheStorageEntry httpCacheStorageEntry, final File outFile) throws Exception { final byte[] bytes = serializer.serialize(httpCacheStorageEntry); try (OutputStream out = new FileOutputStream(outFile)) { out.write(bytes); } } /** * Copy bytes from the given input stream to the given destination buffer until the buffer is full, * or end-of-file is reached, and return the number of bytes read. * * @param src Input stream to read from * @param dest Output buffer to write to * @return Number of bytes read * @throws IOException if an I/O error occurs */ private static int readFully(final InputStream src, final byte[] dest) throws IOException { final int destPos = 0; final int length = dest.length; int totalBytesRead = 0; int lastBytesRead; while (totalBytesRead < length && (lastBytesRead = src.read(dest, destPos + totalBytesRead, length - totalBytesRead)) != -1) { totalBytesRead += lastBytesRead; } return totalBytesRead; } /** * Copy bytes from the given input stream to a new buffer until the given length is reached, * and returns the new buffer. If end-of-file is reached first, an IOException is thrown * * @param src Input stream to read from * @param length Maximum bytes to read * @return All bytes from file * @throws IOException if an I/O error occurs or end-of-file is reached before the requested * number of bytes have been read */ static byte[] readFullyStrict(final InputStream src, final long length) throws IOException { if (length > Integer.MAX_VALUE) { throw new IllegalArgumentException(String.format("Length %d is too large to fit in an array", length)); } final int intLength = (int) length; final byte[] dest = new byte[intLength]; final int bytesRead = readFully(src, dest); if (bytesRead == intLength) { return dest; } else { throw new IOException(String.format("Expected to read %d bytes but only got %d", intLength, bytesRead)); } } } HttpCacheEntryMatcher.java000066400000000000000000000104721434266521000407350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.Arrays; import java.util.Objects; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.http.Header; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class HttpCacheEntryMatcher extends BaseMatcher { private final HttpCacheEntry expectedValue; public HttpCacheEntryMatcher(final HttpCacheEntry expectedValue) { this.expectedValue = expectedValue; } @Override public boolean matches(final Object item) { if (item instanceof HttpCacheEntry) { try { final HttpCacheEntry otherValue = (HttpCacheEntry) item; final int expectedStatus = expectedValue.getStatus(); final int otherStatus = otherValue.getStatus(); if (expectedStatus != otherStatus) { return false; } final Instant expectedRequestInstant = expectedValue.getRequestInstant(); final Instant otherRequestInstant = otherValue.getRequestInstant(); if (!Objects.equals(expectedRequestInstant, otherRequestInstant)) { return false; } final Instant expectedResponseInstant = expectedValue.getResponseInstant(); final Instant otherResponseInstant = otherValue.getResponseInstant(); if (!Objects.equals(expectedResponseInstant, otherResponseInstant)) { return false; } final Header[] expectedHeaders = expectedValue.getHeaders(); final Header[] otherHeaders = otherValue.getHeaders(); if (expectedHeaders.length != otherHeaders.length) { return false; } for (int i = 0; i < expectedHeaders.length; i++) { final Header h1 = expectedHeaders[i]; final Header h2 = otherHeaders[i]; if (!h1.getName().equals(h2.getName()) || !Objects.equals(h1.getValue(), h2.getValue())) { return false; } } final Resource expectedResource = expectedValue.getResource(); final byte[] expectedContent = expectedResource != null ? expectedResource.get() : null; final Resource otherResource = otherValue.getResource(); final byte[] otherContent = otherResource != null ? otherResource.get() : null; return Arrays.equals(expectedContent, otherContent); } catch (final ResourceIOException ex) { throw new RuntimeException(ex); } } return false; } @Override public void describeTo(final Description description) { description.appendValue(expectedValue); } public static Matcher equivalent(final HttpCacheEntry target) { return new HttpCacheEntryMatcher(target); } } HttpTestUtils.java000066400000000000000000000365131434266521000373500ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.InputStream; import java.time.Duration; import java.time.Instant; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Random; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpMessage; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.ByteArrayBuffer; import org.junit.jupiter.api.Assertions; public class HttpTestUtils { /* * "The following HTTP/1.1 headers are hop-by-hop headers..." * * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1 */ private static final String[] HOP_BY_HOP_HEADERS = { "Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailers", "Transfer-Encoding", "Upgrade" }; /* * "Multiple message-header fields with the same field-name MAY be present * in a message if and only if the entire field-value for that header field * is defined as a comma-separated list [i.e., #(values)]." * * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 */ private static final String[] MULTI_HEADERS = { "Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language", "Allow", "Cache-Control", "Connection", "Content-Encoding", "Content-Language", "Expect", "Pragma", "Proxy-Authenticate", "TE", "Trailer", "Transfer-Encoding", "Upgrade", "Via", HttpHeaders.WARNING, "WWW-Authenticate" }; private static final String[] SINGLE_HEADERS = { "Accept-Ranges", "Age", "Authorization", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Date", "ETag", "Expires", "From", "Host", "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Unmodified-Since", "Last-Modified", "Location", "Max-Forwards", "Proxy-Authorization", "Range", "Referer", "Retry-After", "Server", "User-Agent", "Vary" }; /* * Determines whether the given header name is considered a hop-by-hop * header. * * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1 */ public static boolean isHopByHopHeader(final String name) { for (final String s : HOP_BY_HOP_HEADERS) { if (s.equalsIgnoreCase(name)) { return true; } } return false; } /* * Determines whether a given header name may only appear once in a message. */ public static boolean isSingleHeader(final String name) { for (final String s : SINGLE_HEADERS) { if (s.equalsIgnoreCase(name)) { return true; } } return false; } /* * Assertions.asserts that two request or response bodies are byte-equivalent. */ public static boolean equivalent(final HttpEntity e1, final HttpEntity e2) throws Exception { final InputStream i1 = e1.getContent(); final InputStream i2 = e2.getContent(); if (i1 == null && i2 == null) { return true; } if (i1 == null || i2 == null) { return false; // avoid possible NPEs below } int b1 = -1; while ((b1 = i1.read()) != -1) { if (b1 != i2.read()) { return false; } } return (-1 == i2.read()); } /* * Retrieves the full header value by combining multiple headers and * separating with commas, canonicalizing whitespace along the way. * * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 */ public static String getCanonicalHeaderValue(final HttpMessage r, final String name) { if (isSingleHeader(name)) { final Header h = r.getFirstHeader(name); return (h != null) ? h.getValue() : null; } final StringBuilder buf = new StringBuilder(); boolean first = true; for (final Header h : r.getHeaders(name)) { if (!first) { buf.append(", "); } buf.append(h.getValue().trim()); first = false; } return buf.toString(); } /* * Assertions.asserts that all the headers appearing in r1 also appear in r2 * with the same canonical header values. */ public static boolean isEndToEndHeaderSubset(final HttpMessage r1, final HttpMessage r2) { for (final Header h : r1.getHeaders()) { if (!isHopByHopHeader(h.getName())) { final String r1val = getCanonicalHeaderValue(r1, h.getName()); final String r2val = getCanonicalHeaderValue(r2, h.getName()); if (!r1val.equals(r2val)) { return false; } } } return true; } /* * Assertions.asserts that message {@code r2} represents exactly the same * message as {@code r1}, except for hop-by-hop headers. "When a cache * is semantically transparent, the client receives exactly the same * response (except for hop-by-hop headers) that it would have received had * its request been handled directly by the origin server." * * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html#sec1.3 */ public static boolean semanticallyTransparent( final ClassicHttpResponse r1, final ClassicHttpResponse r2) throws Exception { final boolean entitiesEquivalent = equivalent(r1.getEntity(), r2.getEntity()); if (!entitiesEquivalent) { return false; } final boolean statusLinesEquivalent = Objects.equals(r1.getReasonPhrase(), r2.getReasonPhrase()) && r1.getCode() == r2.getCode(); if (!statusLinesEquivalent) { return false; } return isEndToEndHeaderSubset(r1, r2); } /* Assertions.asserts that protocol versions equivalent. */ public static boolean equivalent(final ProtocolVersion v1, final ProtocolVersion v2) { return Objects.equals(v1 != null ? v1 : HttpVersion.DEFAULT, v2 != null ? v2 : HttpVersion.DEFAULT ); } /* Assertions.asserts that two requests are morally equivalent. */ public static boolean equivalent(final HttpRequest r1, final HttpRequest r2) { return equivalent(r1.getVersion(), r2.getVersion()) && Objects.equals(r1.getMethod(), r2.getMethod()) && Objects.equals(r1.getRequestUri(), r2.getRequestUri()) && isEndToEndHeaderSubset(r1, r2); } /* Assertions.asserts that two requests are morally equivalent. */ public static boolean equivalent(final HttpResponse r1, final HttpResponse r2) { return equivalent(r1.getVersion(), r2.getVersion()) && r1.getCode() == r2.getCode() && Objects.equals(r1.getReasonPhrase(), r2.getReasonPhrase()) && isEndToEndHeaderSubset(r1, r2); } public static byte[] getRandomBytes(final int nbytes) { final byte[] bytes = new byte[nbytes]; new Random().nextBytes(bytes); return bytes; } public static ByteArrayBuffer getRandomBuffer(final int nbytes) { final ByteArrayBuffer buf = new ByteArrayBuffer(nbytes); buf.setLength(nbytes); new Random().nextBytes(buf.array()); return buf; } /** Generates a response body with random content. * @param nbytes length of the desired response body * @return an {@link HttpEntity} */ public static HttpEntity makeBody(final int nbytes) { return new ByteArrayEntity(getRandomBytes(nbytes), null); } public static HttpCacheEntry makeCacheEntry(final Instant requestDate, final Instant responseDate) { final Duration diff = Duration.between(requestDate, responseDate); final Instant when = requestDate.plusMillis(diff.toMillis() / 2); return makeCacheEntry(requestDate, responseDate, getStockHeaders(when)); } public static Header[] getStockHeaders(final Instant when) { return new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(when)), new BasicHeader("Server", "MockServer/1.0") }; } public static HttpCacheEntry makeCacheEntry(final Instant requestDate, final Instant responseDate, final Header... headers) { final byte[] bytes = getRandomBytes(128); return makeCacheEntry(requestDate, responseDate, headers, bytes); } public static HttpCacheEntry makeCacheEntry(final Instant requestDate, final Instant responseDate, final Header[] headers, final byte[] bytes) { return makeCacheEntry(requestDate, responseDate, headers, bytes, null); } public static HttpCacheEntry makeCacheEntry(final Map variantMap) { final Instant now = Instant.now(); return makeCacheEntry(now, now, getStockHeaders(now), getRandomBytes(128), variantMap); } @SuppressWarnings("deprecation") public static HttpCacheEntry makeCacheEntry(final Instant requestDate, final Instant responseDate, final Header[] headers, final byte[] bytes, final Map variantMap) { return new HttpCacheEntry(DateUtils.toDate(requestDate), DateUtils.toDate(responseDate), HttpStatus.SC_OK, headers, new HeapResource(bytes), variantMap); } public static HttpCacheEntry makeCacheEntry(final Header[] headers, final byte[] bytes) { final Instant now = Instant.now(); return makeCacheEntry(now, now, headers, bytes); } public static HttpCacheEntry makeCacheEntry(final byte[] bytes) { final Instant now = Instant.now(); return makeCacheEntry(getStockHeaders(now), bytes); } public static HttpCacheEntry makeCacheEntry(final Header... headers) { return makeCacheEntry(headers, getRandomBytes(128)); } public static HttpCacheEntry makeCacheEntry() { final Instant now = Instant.now(); return makeCacheEntry(now, now); } public static ClassicHttpResponse make200Response() { final ClassicHttpResponse out = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); out.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); out.setHeader("Server", "MockOrigin/1.0"); out.setHeader("Content-Length", "128"); out.setEntity(makeBody(128)); return out; } public static final ClassicHttpResponse make200Response(final Instant date, final String cacheControl) { final ClassicHttpResponse response = HttpTestUtils.make200Response(); response.setHeader("Date", DateUtils.formatStandardDate(date)); response.setHeader("Cache-Control",cacheControl); response.setHeader("Etag","\"etag\""); return response; } public static ClassicHttpResponse make304Response() { return new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not modified"); } public static final void assert110WarningFound(final HttpResponse response) { boolean found110Warning = false; final Iterator it = MessageSupport.iterate(response, HttpHeaders.WARNING); while (it.hasNext()) { final HeaderElement elt = it.next(); final String[] parts = elt.getName().split("\\s"); if ("110".equals(parts[0])) { found110Warning = true; break; } } Assertions.assertTrue(found110Warning); } public static ClassicHttpRequest makeDefaultRequest() { return new BasicClassicHttpRequest(Method.GET.toString(), "/"); } public static ClassicHttpRequest makeDefaultHEADRequest() { return new BasicClassicHttpRequest(Method.HEAD.toString(), "/"); } public static ClassicHttpResponse make500Response() { return new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error"); } public static Map makeDefaultVariantMap(final String key, final String value) { final Map variants = new HashMap<>(); variants.put(key, value); return variants; } public static HttpCacheEntry makeCacheEntryWithNoRequestMethodOrEntity(final Header... headers) { final Instant now = Instant.now(); return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null); } public static HttpCacheEntry makeCacheEntryWithNoRequestMethod(final Header... headers) { final Instant now = Instant.now(); return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, new HeapResource(getRandomBytes(128)), null); } public static HttpCacheEntry make204CacheEntryWithNoRequestMethod(final Header... headers) { final Instant now = Instant.now(); return new HttpCacheEntry(now, now, HttpStatus.SC_NO_CONTENT, headers, null, null); } public static HttpCacheEntry makeHeadCacheEntry(final Header... headers) { final Instant now = Instant.now(); return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null); } public static HttpCacheEntry makeHeadCacheEntryWithNoRequestMethod(final Header... headers) { final Instant now = Instant.now(); return new HttpCacheEntry(now, now, HttpStatus.SC_OK, headers, null, null); } } RequestEquivalent.java000066400000000000000000000035621434266521000402340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.core5.http.HttpRequest; import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatchers; public class RequestEquivalent implements ArgumentMatcher { private final T expected; public RequestEquivalent(final T expected) { this.expected = expected; } @Override public boolean matches(final T argument) { if (argument == null) { return false; } return HttpTestUtils.equivalent(expected, argument); } public static T eq(final T request) { return ArgumentMatchers.argThat(new RequestEquivalent<>(request)); } } ResponseEquivalent.java000066400000000000000000000035721434266521000404030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.core5.http.HttpResponse; import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatchers; public class ResponseEquivalent implements ArgumentMatcher { private final T expected; public ResponseEquivalent(final T expected) { this.expected = expected; } @Override public boolean matches(final T argument) { if (argument == null) { return false; } return HttpTestUtils.equivalent(expected, argument); } public static T eq(final T response) { return ArgumentMatchers.argThat(new ResponseEquivalent<>(response)); } } SimpleHttpCacheStorage.java000066400000000000000000000054521434266521000411100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheCASOperation; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceIOException; class SimpleHttpCacheStorage implements HttpCacheStorage { public final Map map; public SimpleHttpCacheStorage() { map = new HashMap<>(); } @Override public void putEntry(final String key, final HttpCacheEntry entry) throws ResourceIOException { map.put(key, entry); } @Override public HttpCacheEntry getEntry(final String key) throws ResourceIOException { return map.get(key); } @Override public void removeEntry(final String key) throws ResourceIOException { map.remove(key); } @Override public void updateEntry( final String key, final HttpCacheCASOperation casOperation) throws ResourceIOException { final HttpCacheEntry v1 = map.get(key); final HttpCacheEntry v2 = casOperation.execute(v1); map.put(key,v2); } @Override public Map getEntries(final Collection keys) throws ResourceIOException { final Map resultMap = new HashMap<>(keys.size()); for (final String key: keys) { final HttpCacheEntry entry = getEntry(key); if (entry != null) { resultMap.put(key, entry); } } return resultMap; } } TestAbstractSerializingAsyncCacheStorage.java000066400000000000000000000514041434266521000446170ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.HttpCacheUpdateException; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; public class TestAbstractSerializingAsyncCacheStorage { @Mock private Cancellable cancellable; @Mock private FutureCallback operationCallback; @Mock private FutureCallback cacheEntryCallback; @Mock private FutureCallback> bulkCacheEntryCallback; private AbstractBinaryAsyncCacheStorage impl; public static byte[] serialize(final String key, final HttpCacheEntry value) throws ResourceIOException { return ByteArrayCacheEntrySerializer.INSTANCE.serialize(new HttpCacheStorageEntry(key, value)); } @BeforeEach @SuppressWarnings("unchecked") public void setUp() { MockitoAnnotations.openMocks(this); impl = Mockito.mock(AbstractBinaryAsyncCacheStorage.class, Mockito.withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS).useConstructor(3)); } @Test public void testCachePut() throws Exception { final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.store( ArgumentMatchers.eq("bar"), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(2); callback.completed(true); return cancellable; }); impl.putEntry(key, value, operationCallback); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte[].class); Mockito.verify(impl).store(ArgumentMatchers.eq("bar"), argumentCaptor.capture(), ArgumentMatchers.any()); Assertions.assertArrayEquals(serialize(key, value), argumentCaptor.getValue()); Mockito.verify(operationCallback).completed(Boolean.TRUE); } @Test public void testCacheGetNullEntry() throws Exception { final String key = "foo"; Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.restore(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed(null); return cancellable; }); impl.getEntry(key, cacheEntryCallback); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(HttpCacheEntry.class); Mockito.verify(cacheEntryCallback).completed(argumentCaptor.capture()); assertThat(argumentCaptor.getValue(), CoreMatchers.nullValue()); Mockito.verify(impl).restore(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); } @Test public void testCacheGet() throws Exception { final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.restore(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed(serialize(key, value)); return cancellable; }); impl.getEntry(key, cacheEntryCallback); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(HttpCacheEntry.class); Mockito.verify(cacheEntryCallback).completed(argumentCaptor.capture()); final HttpCacheEntry resultingEntry = argumentCaptor.getValue(); assertThat(resultingEntry, HttpCacheEntryMatcher.equivalent(value)); Mockito.verify(impl).restore(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); } @Test public void testCacheGetKeyMismatch() throws Exception { final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.restore(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed(serialize("not-foo", value)); return cancellable; }); impl.getEntry(key, cacheEntryCallback); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(HttpCacheEntry.class); Mockito.verify(cacheEntryCallback).completed(argumentCaptor.capture()); assertThat(argumentCaptor.getValue(), CoreMatchers.nullValue()); Mockito.verify(impl).restore(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); } @Test public void testCacheRemove() throws Exception{ final String key = "foo"; Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.delete( ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed(true); return cancellable; }); impl.removeEntry(key, operationCallback); Mockito.verify(impl).delete("bar", operationCallback); Mockito.verify(operationCallback).completed(Boolean.TRUE); } @Test public void testCacheUpdateNullEntry() throws Exception { final String key = "foo"; final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed(null); return cancellable; }); Mockito.when(impl.store( ArgumentMatchers.eq("bar"), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(2); callback.completed(true); return cancellable; }); impl.updateEntry(key, existing -> { assertThat(existing, CoreMatchers.nullValue()); return updatedValue; }, operationCallback); Mockito.verify(impl).getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); Mockito.verify(impl).store(ArgumentMatchers.eq("bar"), ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verify(operationCallback).completed(Boolean.TRUE); } @Test public void testCacheCASUpdate() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed("stuff"); return cancellable; }); Mockito.when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue)); Mockito.when(impl.updateCAS( ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(3); callback.completed(true); return cancellable; }); impl.updateEntry(key, existing -> updatedValue, operationCallback); Mockito.verify(impl).getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); Mockito.verify(impl).getStorageObject("stuff"); Mockito.verify(impl).updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verify(operationCallback).completed(Boolean.TRUE); } @Test public void testCacheCASUpdateKeyMismatch() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer( (Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed("stuff"); return cancellable; }); Mockito.when(impl.getStorageObject("stuff")).thenReturn(serialize("not-foo", existingValue)); Mockito.when(impl.store( ArgumentMatchers.eq("bar"), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(2); callback.completed(true); return cancellable; }); impl.updateEntry(key, existing -> { assertThat(existing, CoreMatchers.nullValue()); return updatedValue; }, operationCallback); Mockito.verify(impl).getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); Mockito.verify(impl).getStorageObject("stuff"); Mockito.verify(impl, Mockito.never()).updateCAS( ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verify(impl).store(ArgumentMatchers.eq("bar"), ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verify(operationCallback).completed(Boolean.TRUE); } @Test public void testSingleCacheUpdateRetry() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer( (Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed("stuff"); return cancellable; }); Mockito.when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue)); final AtomicInteger count = new AtomicInteger(0); Mockito.when(impl.updateCAS( ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(3); if (count.incrementAndGet() == 1) { callback.completed(false); } else { callback.completed(true); } return cancellable; }); impl.updateEntry(key, existing -> updatedValue, operationCallback); Mockito.verify(impl, Mockito.times(2)).getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); Mockito.verify(impl, Mockito.times(2)).getStorageObject("stuff"); Mockito.verify(impl, Mockito.times(2)).updateCAS( ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verify(operationCallback).completed(Boolean.TRUE); } @Test public void testCacheUpdateFail() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); Mockito.when(impl.digestToStorageKey(key)).thenReturn("bar"); Mockito.when(impl.getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any())).thenAnswer( (Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed("stuff"); return cancellable; }); Mockito.when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue)); final AtomicInteger count = new AtomicInteger(0); Mockito.when(impl.updateCAS( ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(3); if (count.incrementAndGet() <= 3) { callback.completed(false); } else { callback.completed(true); } return cancellable; }); impl.updateEntry(key, existing -> updatedValue, operationCallback); Mockito.verify(impl, Mockito.times(3)).getForUpdateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); Mockito.verify(impl, Mockito.times(3)).getStorageObject("stuff"); Mockito.verify(impl, Mockito.times(3)).updateCAS( ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verify(operationCallback).failed(ArgumentMatchers.any()); } @Test @SuppressWarnings("unchecked") public void testBulkGet() throws Exception { final String key1 = "foo this"; final String key2 = "foo that"; final String storageKey1 = "bar this"; final String storageKey2 = "bar that"; final HttpCacheEntry value1 = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry value2 = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key1)).thenReturn(storageKey1); when(impl.digestToStorageKey(key2)).thenReturn(storageKey2); when(impl.bulkRestore( ArgumentMatchers.anyCollection(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final Collection keys = invocation.getArgument(0); final FutureCallback> callback = invocation.getArgument(1); final Map resultMap = new HashMap<>(); if (keys.contains(storageKey1)) { resultMap.put(storageKey1, serialize(key1, value1)); } if (keys.contains(storageKey2)) { resultMap.put(storageKey2, serialize(key2, value2)); } callback.completed(resultMap); return cancellable; }); impl.getEntries(Arrays.asList(key1, key2), bulkCacheEntryCallback); final ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(Map.class); Mockito.verify(bulkCacheEntryCallback).completed(argumentCaptor.capture()); final Map entryMap = argumentCaptor.getValue(); assertThat(entryMap, CoreMatchers.notNullValue()); assertThat(entryMap.get(key1), HttpCacheEntryMatcher.equivalent(value1)); assertThat(entryMap.get(key2), HttpCacheEntryMatcher.equivalent(value2)); verify(impl, Mockito.times(2)).digestToStorageKey(key1); verify(impl, Mockito.times(2)).digestToStorageKey(key2); verify(impl).bulkRestore( ArgumentMatchers.eq(Arrays.asList(storageKey1, storageKey2)), ArgumentMatchers.any()); } @Test @SuppressWarnings("unchecked") public void testBulkGetKeyMismatch() throws Exception { final String key1 = "foo this"; final String key2 = "foo that"; final String storageKey1 = "bar this"; final String storageKey2 = "bar that"; final HttpCacheEntry value1 = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry value2 = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key1)).thenReturn(storageKey1); when(impl.digestToStorageKey(key2)).thenReturn(storageKey2); when(impl.bulkRestore( ArgumentMatchers.anyCollection(), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final Collection keys = invocation.getArgument(0); final FutureCallback> callback = invocation.getArgument(1); final Map resultMap = new HashMap<>(); if (keys.contains(storageKey1)) { resultMap.put(storageKey1, serialize(key1, value1)); } if (keys.contains(storageKey2)) { resultMap.put(storageKey2, serialize("not foo", value2)); } callback.completed(resultMap); return cancellable; }); impl.getEntries(Arrays.asList(key1, key2), bulkCacheEntryCallback); final ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(Map.class); Mockito.verify(bulkCacheEntryCallback).completed(argumentCaptor.capture()); final Map entryMap = argumentCaptor.getValue(); assertThat(entryMap, CoreMatchers.notNullValue()); assertThat(entryMap.get(key1), HttpCacheEntryMatcher.equivalent(value1)); assertThat(entryMap.get(key2), CoreMatchers.nullValue()); verify(impl, Mockito.times(2)).digestToStorageKey(key1); verify(impl, Mockito.times(2)).digestToStorageKey(key2); verify(impl).bulkRestore( ArgumentMatchers.eq(Arrays.asList(storageKey1, storageKey2)), ArgumentMatchers.any()); } } TestAbstractSerializingCacheStorage.java000066400000000000000000000301471434266521000436220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.HttpCacheUpdateException; import org.apache.hc.client5.http.cache.ResourceIOException; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import org.mockito.stubbing.Answer; @SuppressWarnings("boxing") // test code public class TestAbstractSerializingCacheStorage { public static byte[] serialize(final String key, final HttpCacheEntry value) throws ResourceIOException { return ByteArrayCacheEntrySerializer.INSTANCE.serialize(new HttpCacheStorageEntry(key, value)); } private AbstractBinaryCacheStorage impl; @BeforeEach @SuppressWarnings("unchecked") public void setUp() { impl = Mockito.mock(AbstractBinaryCacheStorage.class, Mockito.withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS).useConstructor(3)); } @Test public void testCachePut() throws Exception { final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); impl.putEntry(key, value); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(byte[].class); verify(impl).store(eq("bar"), argumentCaptor.capture()); Assertions.assertArrayEquals(serialize(key, value), argumentCaptor.getValue()); } @Test public void testCacheGetNullEntry() throws Exception { final String key = "foo"; when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.restore("bar")).thenReturn(null); final HttpCacheEntry resultingEntry = impl.getEntry(key); verify(impl).restore("bar"); assertThat(resultingEntry, CoreMatchers.nullValue()); } @Test public void testCacheGet() throws Exception { final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.restore("bar")).thenReturn(serialize(key, value)); final HttpCacheEntry resultingEntry = impl.getEntry(key); verify(impl).restore("bar"); assertThat(resultingEntry, HttpCacheEntryMatcher.equivalent(value)); } @Test public void testCacheGetKeyMismatch() throws Exception { final String key = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.restore("bar")).thenReturn(serialize("not-foo", value)); final HttpCacheEntry resultingEntry = impl.getEntry(key); verify(impl).restore("bar"); assertThat(resultingEntry, CoreMatchers.nullValue()); } @Test public void testCacheRemove() throws Exception{ final String key = "foo"; when(impl.digestToStorageKey(key)).thenReturn("bar"); impl.removeEntry(key); verify(impl).delete("bar"); } @Test public void testCacheUpdateNullEntry() throws Exception { final String key = "foo"; final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.getForUpdateCAS("bar")).thenReturn(null); impl.updateEntry(key, existing -> { assertThat(existing, CoreMatchers.nullValue()); return updatedValue; }); verify(impl).getForUpdateCAS("bar"); verify(impl).store(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); } @Test public void testCacheCASUpdate() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.getForUpdateCAS("bar")).thenReturn("stuff"); when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue)); when(impl.updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any())).thenReturn(true); impl.updateEntry(key, existing -> updatedValue); verify(impl).getForUpdateCAS("bar"); verify(impl).getStorageObject("stuff"); verify(impl).updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any()); } @Test public void testCacheCASUpdateKeyMismatch() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.getForUpdateCAS("bar")).thenReturn("stuff"); when(impl.getStorageObject("stuff")).thenReturn(serialize("not-foo", existingValue)); when(impl.updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any())).thenReturn(true); impl.updateEntry(key, existing -> { assertThat(existing, CoreMatchers.nullValue()); return updatedValue; }); verify(impl).getForUpdateCAS("bar"); verify(impl).getStorageObject("stuff"); verify(impl).store(ArgumentMatchers.eq("bar"), ArgumentMatchers.any()); } @Test public void testSingleCacheUpdateRetry() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.getForUpdateCAS("bar")).thenReturn("stuff"); when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue)); when(impl.updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any())).thenReturn(false, true); impl.updateEntry(key, existing -> updatedValue); verify(impl, Mockito.times(2)).getForUpdateCAS("bar"); verify(impl, Mockito.times(2)).getStorageObject("stuff"); verify(impl, Mockito.times(2)).updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any()); } @Test public void testCacheUpdateFail() throws Exception { final String key = "foo"; final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key)).thenReturn("bar"); when(impl.getForUpdateCAS("bar")).thenReturn("stuff"); when(impl.getStorageObject("stuff")).thenReturn(serialize(key, existingValue)); when(impl.updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any())) .thenReturn(false, false, false, true); Assertions.assertThrows(HttpCacheUpdateException.class, () -> impl.updateEntry(key, existing -> updatedValue)); verify(impl, Mockito.times(3)).getForUpdateCAS("bar"); verify(impl, Mockito.times(3)).getStorageObject("stuff"); verify(impl, Mockito.times(3)).updateCAS(ArgumentMatchers.eq("bar"), ArgumentMatchers.eq("stuff"), ArgumentMatchers.any()); } @Test public void testBulkGet() throws Exception { final String key1 = "foo this"; final String key2 = "foo that"; final String storageKey1 = "bar this"; final String storageKey2 = "bar that"; final HttpCacheEntry value1 = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry value2 = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key1)).thenReturn(storageKey1); when(impl.digestToStorageKey(key2)).thenReturn(storageKey2); when(impl.bulkRestore(ArgumentMatchers.anyCollection())).thenAnswer((Answer>) invocation -> { final Collection keys = invocation.getArgument(0); final Map resultMap = new HashMap<>(); if (keys.contains(storageKey1)) { resultMap.put(storageKey1, serialize(key1, value1)); } if (keys.contains(storageKey2)) { resultMap.put(storageKey2, serialize(key2, value2)); } return resultMap; }); final Map entryMap = impl.getEntries(Arrays.asList(key1, key2)); assertThat(entryMap, CoreMatchers.notNullValue()); assertThat(entryMap.get(key1), HttpCacheEntryMatcher.equivalent(value1)); assertThat(entryMap.get(key2), HttpCacheEntryMatcher.equivalent(value2)); verify(impl, Mockito.times(2)).digestToStorageKey(key1); verify(impl, Mockito.times(2)).digestToStorageKey(key2); verify(impl).bulkRestore(Arrays.asList(storageKey1, storageKey2)); } @Test public void testBulkGetKeyMismatch() throws Exception { final String key1 = "foo this"; final String key2 = "foo that"; final String storageKey1 = "bar this"; final String storageKey2 = "bar that"; final HttpCacheEntry value1 = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry value2 = HttpTestUtils.makeCacheEntry(); when(impl.digestToStorageKey(key1)).thenReturn(storageKey1); when(impl.digestToStorageKey(key2)).thenReturn(storageKey2); when(impl.bulkRestore(ArgumentMatchers.anyCollection())).thenAnswer((Answer>) invocation -> { final Collection keys = invocation.getArgument(0); final Map resultMap = new HashMap<>(); if (keys.contains(storageKey1)) { resultMap.put(storageKey1, serialize(key1, value1)); } if (keys.contains(storageKey2)) { resultMap.put(storageKey2, serialize("not foo", value2)); } return resultMap; }); final Map entryMap = impl.getEntries(Arrays.asList(key1, key2)); assertThat(entryMap, CoreMatchers.notNullValue()); assertThat(entryMap.get(key1), HttpCacheEntryMatcher.equivalent(value1)); assertThat(entryMap.get(key2), CoreMatchers.nullValue()); verify(impl, Mockito.times(2)).digestToStorageKey(key1); verify(impl, Mockito.times(2)).digestToStorageKey(key2); verify(impl).bulkRestore(Arrays.asList(storageKey1, storageKey2)); } } TestBasicHttpCache.java000066400000000000000000000306271434266521000402150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import java.time.Instant; import java.util.Map; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.classic.methods.HttpDelete; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpHead; import org.apache.hc.client5.http.classic.methods.HttpOptions; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpTrace; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.util.ByteArrayBuffer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestBasicHttpCache { private BasicHttpCache impl; private SimpleHttpCacheStorage backing; @BeforeEach public void setUp() throws Exception { backing = new SimpleHttpCacheStorage(); impl = new BasicHttpCache(new HeapResourceFactory(), backing); } @Test public void testDoNotFlushCacheEntriesOnGet() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpGet("/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); backing.map.put(key, entry); impl.flushCacheEntriesFor(host, req); assertEquals(entry, backing.map.get(key)); } @Test public void testDoNotFlushCacheEntriesOnHead() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpHead("/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); backing.map.put(key, entry); impl.flushCacheEntriesFor(host, req); assertEquals(entry, backing.map.get(key)); } @Test public void testDoNotFlushCacheEntriesOnOptions() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpOptions("/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); backing.map.put(key, entry); impl.flushCacheEntriesFor(host, req); assertEquals(entry, backing.map.get(key)); } @Test public void testDoNotFlushCacheEntriesOnTrace() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpTrace("/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); backing.map.put(key, entry); impl.flushCacheEntriesFor(host, req); assertEquals(entry, backing.map.get(key)); } @Test public void testFlushContentLocationEntryIfUnSafeRequest() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpPost("/foo"); final HttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Content-Location", "/bar"); resp.setHeader(HeaderConstants.ETAG, "\"etag\""); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, new HttpGet("/bar")); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("ETag", "\"old-etag\"") }); backing.map.put(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, req, resp); assertNull(backing.map.get(key)); } @Test public void testDoNotFlushContentLocationEntryIfSafeRequest() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpGet("/foo"); final HttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Content-Location", "/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, new HttpGet("/bar")); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("ETag", "\"old-etag\"") }); backing.map.put(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, req, resp); assertEquals(entry, backing.map.get(key)); } @Test public void testCanFlushCacheEntriesAtUri() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpDelete("/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); backing.map.put(key, entry); impl.flushCacheEntriesFor(host, req); assertNull(backing.map.get(key)); } @Test public void testStoreInCachePutsNonVariantEntryInPlace() throws Exception { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); assertFalse(entry.hasVariants()); final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpGet("http://foo.example.com/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req); impl.storeInCache(key, host, req, entry); assertSame(entry, backing.map.get(key)); } @Test public void testGetCacheEntryReturnsNullOnCacheMiss() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest request = new HttpGet("http://foo.example.com/bar"); final HttpCacheEntry result = impl.getCacheEntry(host, request); assertNull(result); } @Test public void testGetCacheEntryFetchesFromCacheOnCacheHitIfNoVariants() throws Exception { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); assertFalse(entry.hasVariants()); final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest request = new HttpGet("http://foo.example.com/bar"); final String key = CacheKeyGenerator.INSTANCE.generateKey(host, request); backing.map.put(key,entry); final HttpCacheEntry result = impl.getCacheEntry(host, request); assertSame(entry, result); } @Test public void testGetCacheEntryReturnsNullIfNoVariantInCache() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar"); origRequest.setHeader("Accept-Encoding","gzip"); final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128); final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); origResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); origResponse.setHeader("Cache-Control", "max-age=3600, public"); origResponse.setHeader("ETag", "\"etag\""); origResponse.setHeader("Vary", "Accept-Encoding"); origResponse.setHeader("Content-Encoding","gzip"); impl.createCacheEntry(host, origRequest, origResponse, buf, Instant.now(), Instant.now()); final HttpRequest request = new HttpGet("http://foo.example.com/bar"); final HttpCacheEntry result = impl.getCacheEntry(host, request); assertNull(result); } @Test public void testGetCacheEntryReturnsVariantIfPresentInCache() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar"); origRequest.setHeader("Accept-Encoding","gzip"); final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128); final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); origResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); origResponse.setHeader("Cache-Control", "max-age=3600, public"); origResponse.setHeader("ETag", "\"etag\""); origResponse.setHeader("Vary", "Accept-Encoding"); origResponse.setHeader("Content-Encoding","gzip"); impl.createCacheEntry(host, origRequest, origResponse, buf, Instant.now(), Instant.now()); final HttpRequest request = new HttpGet("http://foo.example.com/bar"); request.setHeader("Accept-Encoding","gzip"); final HttpCacheEntry result = impl.getCacheEntry(host, request); assertNotNull(result); } @Test public void testGetVariantCacheEntriesReturnsEmptySetOnNoVariants() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest request = new HttpGet("http://foo.example.com/bar"); final Map variants = impl.getVariantCacheEntriesWithEtags(host, request); assertNotNull(variants); assertEquals(0, variants.size()); } @Test public void testGetVariantCacheEntriesReturnsAllVariants() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new HttpGet("http://foo.example.com/bar"); req1.setHeader("Accept-Encoding", "gzip"); final HttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "max-age=3600, public"); resp1.setHeader("ETag", "\"etag1\""); resp1.setHeader("Vary", "Accept-Encoding"); resp1.setHeader("Content-Encoding","gzip"); resp1.setHeader("Vary", "Accept-Encoding"); final HttpRequest req2 = new HttpGet("http://foo.example.com/bar"); req2.setHeader("Accept-Encoding", "identity"); final HttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp2.setHeader("Cache-Control", "max-age=3600, public"); resp2.setHeader("ETag", "\"etag2\""); resp2.setHeader("Vary", "Accept-Encoding"); resp2.setHeader("Content-Encoding","gzip"); resp2.setHeader("Vary", "Accept-Encoding"); impl.createCacheEntry(host, req1, resp1, null, Instant.now(), Instant.now()); impl.createCacheEntry(host, req2, resp2, null, Instant.now(), Instant.now()); final Map variants = impl.getVariantCacheEntriesWithEtags(host, req1); assertNotNull(variants); assertEquals(2, variants.size()); } } TestByteArrayCacheEntrySerializer.java000066400000000000000000000244361434266521000433130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.StatusLine; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestByteArrayCacheEntrySerializer { private ByteArrayCacheEntrySerializer impl; @BeforeEach public void setUp() { impl = new ByteArrayCacheEntrySerializer(); } @Test public void canSerializeEntriesWithVariantMapsDeprecatedConstructor() throws Exception { readWriteVerify(makeCacheEntryDeprecatedConstructorWithVariantMap("somekey")); } @Test public void canSerializeEntriesWithVariantMapsAndInstant() throws Exception { readWriteVerify(makeCacheEntryWithVariantMap("somekey")); } @Test public void isAllowedClassNameStringTrue() { assertIsAllowedClassNameTrue(String.class.getName()); } @Test public void isAllowedClassNameStringArrayTrue() { assertIsAllowedClassNameTrue("[L" + String.class.getName()); } @Test public void isAllowedClassNameStringArrayArrayTrue() { assertIsAllowedClassNameTrue("[[L" + String.class.getName()); } @Test public void isAllowedClassNameDataTrue() { assertIsAllowedClassNameTrue(Date.class.getName()); } @Test public void isAllowedClassNameInstantTrue() { assertIsAllowedClassNameTrue(Instant.class.getName()); } @Test public void isAllowedClassNameStatusLineTrue() { assertIsAllowedClassNameTrue(StatusLine.class.getName()); } @Test public void isAllowedClassNameResourceTrue() { assertIsAllowedClassNameTrue(Resource.class.getName()); } @Test public void isAllowedClassNameByteArrayTrue() { assertIsAllowedClassNameTrue("[B"); } @Test public void isAllowedClassNameByteArrayArrayTrue() { assertIsAllowedClassNameTrue("[[B"); } @Test public void isAllowedClassNameCharArrayTrue() { assertIsAllowedClassNameTrue("[C"); } @Test public void isAllowedClassNameCharArrayArrayTrue() { assertIsAllowedClassNameTrue("[[C"); } @Test public void isAllowedClassNameDoubleArrayTrue() { assertIsAllowedClassNameTrue("[D"); } @Test public void isAllowedClassNameDoubleArrayArrayTrue() { assertIsAllowedClassNameTrue("[[D"); } @Test public void isAllowedClassNameFloatArrayTrue() { assertIsAllowedClassNameTrue("[F"); } @Test public void isAllowedClassNameFloatArrayArrayTrue() { assertIsAllowedClassNameTrue("[[F"); } @Test public void isAllowedClassNameIntArrayTrue() { assertIsAllowedClassNameTrue("[I"); } @Test public void isAllowedClassNameIntArrayArrayTrue() { assertIsAllowedClassNameTrue("[[I"); } @Test public void isAllowedClassNameLongArrayTrue() { assertIsAllowedClassNameTrue("[J"); } @Test public void isAllowedClassNameLongArrayArrayTrue() { assertIsAllowedClassNameTrue("[[J"); } @Test public void isAllowedClassNameShortArrayTrue() { assertIsAllowedClassNameTrue("[S"); } @Test public void isAllowedClassNameShortArrayArrayTrue() { assertIsAllowedClassNameTrue("[[S"); } @Test public void isAllowedClassNameCollectionsInvokerTransformerFalse() { assertIsAllowedClassNameFalse("org.apache.commons.collections.functors.InvokerTransformer"); } @Test public void isAllowedClassNameCollections4InvokerTransformerFalse() { assertIsAllowedClassNameFalse("org.apache.commons.collections4.functors.InvokerTransformer"); } @Test public void isAllowedClassNameCollectionsInstantiateTransformerFalse() { assertIsAllowedClassNameFalse("org.apache.commons.collections.functors.InstantiateTransformer"); } @Test public void isAllowedClassNameCollections4InstantiateTransformerFalse() { assertIsAllowedClassNameFalse("org.apache.commons.collections4.functors.InstantiateTransformer"); } @Test public void isAllowedClassNameGroovyConvertedClosureFalse() { assertIsAllowedClassNameFalse("org.codehaus.groovy.runtime.ConvertedClosure"); } @Test public void isAllowedClassNameGroovyMethodClosureFalse() { assertIsAllowedClassNameFalse("org.codehaus.groovy.runtime.MethodClosure"); } @Test public void isAllowedClassNameSpringObjectFactoryFalse() { assertIsAllowedClassNameFalse("org.springframework.beans.factory.ObjectFactory"); } @Test public void isAllowedClassNameCalanTemplatesImplFalse() { assertIsAllowedClassNameFalse("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); } @Test public void isAllowedClassNameCalanTemplatesImplArrayFalse() { assertIsAllowedClassNameFalse("[Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); } @Test public void isAllowedClassNameJavaRmiRegistryFalse() { assertIsAllowedClassNameFalse("java.rmi.registry.Registry"); } @Test public void isAllowedClassNameJavaRmiServerRemoteObjectInvocationHandlerFalse() { assertIsAllowedClassNameFalse("java.rmi.server.RemoteObjectInvocationHandler"); } @Test public void isAllowedClassNameJavaxXmlTransformTemplatesFalse() { assertIsAllowedClassNameFalse("javax.xml.transform.Templates"); } @Test public void isAllowedClassNameJavaxManagementMBeanServerInvocationHandlerFalse() { assertIsAllowedClassNameFalse("javax.management.MBeanServerInvocationHandler"); } private static void assertIsAllowedClassNameTrue(final String className) { assertTrue(ByteArrayCacheEntrySerializer.RestrictedObjectInputStream.isAllowedClassName(className)); } private static void assertIsAllowedClassNameFalse(final String className) { assertFalse(ByteArrayCacheEntrySerializer.RestrictedObjectInputStream.isAllowedClassName(className)); } private byte[] serializeProhibitedObject() throws IOException { final BigDecimal bigDecimal = new BigDecimal("1000.00"); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(bigDecimal); } return baos.toByteArray(); } public void readWriteVerify(final HttpCacheStorageEntry writeEntry) throws Exception { // write the entry final byte[] bytes = impl.serialize(writeEntry); // read the entry final HttpCacheStorageEntry readEntry = impl.deserialize(bytes); // compare assertEquals(readEntry.getKey(), writeEntry.getKey()); assertThat(readEntry.getContent(), HttpCacheEntryMatcher.equivalent(writeEntry.getContent())); } private HttpCacheStorageEntry makeCacheEntryDeprecatedConstructorWithVariantMap(final String key) { final Header[] headers = new Header[5]; for (int i = 0; i < headers.length; i++) { headers[i] = new BasicHeader("header" + i, "value" + i); } final String body = "Lorem ipsum dolor sit amet"; final Map variantMap = new HashMap<>(); variantMap.put("test variant 1","true"); variantMap.put("test variant 2","true"); final HttpCacheEntry cacheEntry = new HttpCacheEntry( Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, new HeapResource(body.getBytes(StandardCharsets.UTF_8)), variantMap); return new HttpCacheStorageEntry(key, cacheEntry); } private HttpCacheStorageEntry makeCacheEntryWithVariantMap(final String key) { final Header[] headers = new Header[5]; for (int i = 0; i < headers.length; i++) { headers[i] = new BasicHeader("header" + i, "value" + i); } final String body = "Lorem ipsum dolor sit amet"; final Map variantMap = new HashMap<>(); variantMap.put("test variant 1","true"); variantMap.put("test variant 2","true"); final HttpCacheEntry cacheEntry = new HttpCacheEntry( Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, new HeapResource(body.getBytes(StandardCharsets.UTF_8)), variantMap); return new HttpCacheStorageEntry(key, cacheEntry); } } TestCacheKeyGenerator.java000066400000000000000000000466351434266521000407410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHeaderIterator; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @SuppressWarnings({"boxing","static-access"}) // this is test code public class TestCacheKeyGenerator { private static final BasicHttpRequest REQUEST_FULL_EPISODES = new BasicHttpRequest("GET", "/full_episodes"); private static final BasicHttpRequest REQUEST_ROOT = new BasicHttpRequest("GET", "/"); private CacheKeyGenerator extractor; private HttpHost defaultHost; private HttpCacheEntry mockEntry; private HttpRequest mockRequest; @BeforeEach public void setUp() throws Exception { defaultHost = new HttpHost("foo.example.com"); mockEntry = mock(HttpCacheEntry.class); mockRequest = mock(HttpRequest.class); extractor = CacheKeyGenerator.INSTANCE; } @Test public void testExtractsUriFromAbsoluteUriInRequest() { final HttpHost host = new HttpHost("bar.example.com"); final HttpRequest req = new HttpGet("http://foo.example.com/"); Assertions.assertEquals("http://foo.example.com:80/", extractor.generateKey(host, req)); } @Test public void testGetURIWithDefaultPortAndScheme() { Assertions.assertEquals("http://www.comcast.net:80/", extractor.generateKey(new HttpHost( "www.comcast.net"), REQUEST_ROOT)); Assertions.assertEquals("http://www.fancast.com:80/full_episodes", extractor.generateKey(new HttpHost( "www.fancast.com"), REQUEST_FULL_EPISODES)); } @Test public void testGetURIWithDifferentScheme() { Assertions.assertEquals("https://www.comcast.net:443/", extractor.generateKey( new HttpHost("https", "www.comcast.net", -1), REQUEST_ROOT)); Assertions.assertEquals("myhttp://www.fancast.com/full_episodes", extractor.generateKey( new HttpHost("myhttp", "www.fancast.com", -1), REQUEST_FULL_EPISODES)); } @Test public void testGetURIWithDifferentPort() { Assertions.assertEquals("http://www.comcast.net:8080/", extractor.generateKey(new HttpHost( "www.comcast.net", 8080), REQUEST_ROOT)); Assertions.assertEquals("http://www.fancast.com:9999/full_episodes", extractor.generateKey( new HttpHost("www.fancast.com", 9999), REQUEST_FULL_EPISODES)); } @Test public void testGetURIWithDifferentPortAndScheme() { Assertions.assertEquals("https://www.comcast.net:8080/", extractor.generateKey( new HttpHost("https", "www.comcast.net", 8080), REQUEST_ROOT)); Assertions.assertEquals("myhttp://www.fancast.com:9999/full_episodes", extractor.generateKey( new HttpHost("myhttp", "www.fancast.com", 9999), REQUEST_FULL_EPISODES)); } @Test public void testGetURIWithQueryParameters() { Assertions.assertEquals("http://www.comcast.net:80/?foo=bar", extractor.generateKey( new HttpHost("http", "www.comcast.net", -1), new BasicHttpRequest("GET", "/?foo=bar"))); Assertions.assertEquals("http://www.fancast.com:80/full_episodes?foo=bar", extractor.generateKey( new HttpHost("http", "www.fancast.com", -1), new BasicHttpRequest("GET", "/full_episodes?foo=bar"))); } @Test public void testGetVariantURIWithNoVaryHeaderReturnsNormalURI() { final String theURI = "theURI"; when(mockEntry.hasVariants()).thenReturn(false); extractor = new CacheKeyGenerator() { @Override public String generateKey(final HttpHost h, final HttpRequest request) { Assertions.assertSame(defaultHost, h); Assertions.assertSame(mockRequest, request); return theURI; } }; final String result = extractor.generateKey(defaultHost, mockRequest, mockEntry); verify(mockEntry).hasVariants(); Assertions.assertSame(theURI, result); } @Test public void testGetVariantURIWithSingleValueVaryHeaderPrepends() { final String theURI = "theURI"; final Header[] varyHeaders = { new BasicHeader("Vary", "Accept-Encoding") }; final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip") }; extractor = new CacheKeyGenerator() { @Override public String generateKey(final HttpHost h, final HttpRequest request) { Assertions.assertSame(defaultHost, h); Assertions.assertSame(mockRequest, request); return theURI; } }; when(mockEntry.hasVariants()).thenReturn(true); when(mockEntry.headerIterator("Vary")).thenReturn(new BasicHeaderIterator(varyHeaders, "Vary")); when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(encHeaders); final String result = extractor.generateKey(defaultHost, mockRequest, mockEntry); verify(mockEntry).hasVariants(); verify(mockEntry).headerIterator("Vary"); verify(mockRequest).getHeaders("Accept-Encoding"); Assertions.assertEquals("{Accept-Encoding=gzip}" + theURI, result); } @Test public void testGetVariantURIWithMissingRequestHeader() { final String theURI = "theURI"; final Header[] noHeaders = new Header[0]; final Header[] varyHeaders = { new BasicHeader("Vary", "Accept-Encoding") }; extractor = new CacheKeyGenerator() { @Override public String generateKey(final HttpHost h, final HttpRequest request) { Assertions.assertSame(defaultHost, h); Assertions.assertSame(mockRequest, request); return theURI; } }; when(mockEntry.hasVariants()).thenReturn(true); when(mockEntry.headerIterator("Vary")).thenReturn(new BasicHeaderIterator(varyHeaders, "Vary")); when(mockRequest.getHeaders("Accept-Encoding")) .thenReturn(noHeaders); final String result = extractor.generateKey(defaultHost, mockRequest, mockEntry); verify(mockEntry).hasVariants(); verify(mockEntry).headerIterator("Vary"); verify(mockRequest).getHeaders("Accept-Encoding"); Assertions.assertEquals("{Accept-Encoding=}" + theURI, result); } @Test public void testGetVariantURIAlphabetizesWithMultipleVaryingHeaders() { final String theURI = "theURI"; final Header[] varyHeaders = { new BasicHeader("Vary", "User-Agent, Accept-Encoding") }; final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip") }; final Header[] uaHeaders = { new BasicHeader("User-Agent", "browser") }; extractor = new CacheKeyGenerator() { @Override public String generateKey(final HttpHost h, final HttpRequest request) { Assertions.assertSame(defaultHost, h); Assertions.assertSame(mockRequest, request); return theURI; } }; when(mockEntry.hasVariants()).thenReturn(true); when(mockEntry.headerIterator("Vary")).thenReturn(new BasicHeaderIterator(varyHeaders, "Vary")); when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(encHeaders); when(mockRequest.getHeaders("User-Agent")).thenReturn(uaHeaders); final String result = extractor.generateKey(defaultHost, mockRequest, mockEntry); verify(mockEntry).hasVariants(); verify(mockEntry).headerIterator("Vary"); verify(mockRequest).getHeaders("Accept-Encoding"); verify(mockRequest).getHeaders("User-Agent"); Assertions.assertEquals("{Accept-Encoding=gzip&User-Agent=browser}" + theURI, result); } @Test public void testGetVariantURIHandlesMultipleVaryHeaders() { final String theURI = "theURI"; final Header[] varyHeaders = { new BasicHeader("Vary", "User-Agent"), new BasicHeader("Vary", "Accept-Encoding") }; final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip") }; final Header[] uaHeaders = { new BasicHeader("User-Agent", "browser") }; extractor = new CacheKeyGenerator() { @Override public String generateKey(final HttpHost h, final HttpRequest request) { Assertions.assertSame(defaultHost, h); Assertions.assertSame(mockRequest, request); return theURI; } }; when(mockEntry.hasVariants()).thenReturn(true); when(mockEntry.headerIterator("Vary")).thenReturn(new BasicHeaderIterator(varyHeaders, "Vary")); when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(encHeaders); when(mockRequest.getHeaders("User-Agent")).thenReturn(uaHeaders); final String result = extractor.generateKey(defaultHost, mockRequest, mockEntry); verify(mockEntry).hasVariants(); verify(mockEntry).headerIterator("Vary"); verify(mockRequest).getHeaders("Accept-Encoding"); verify(mockRequest).getHeaders("User-Agent"); Assertions.assertEquals("{Accept-Encoding=gzip&User-Agent=browser}" + theURI, result); } @Test public void testGetVariantURIHandlesMultipleLineRequestHeaders() { final String theURI = "theURI"; final Header[] varyHeaders = { new BasicHeader("Vary", "User-Agent, Accept-Encoding") }; final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip"), new BasicHeader("Accept-Encoding", "deflate") }; final Header[] uaHeaders = { new BasicHeader("User-Agent", "browser") }; extractor = new CacheKeyGenerator() { @Override public String generateKey(final HttpHost h, final HttpRequest request) { Assertions.assertSame(defaultHost, h); Assertions.assertSame(mockRequest, request); return theURI; } }; when(mockEntry.hasVariants()).thenReturn(true); when(mockEntry.headerIterator("Vary")).thenReturn(new BasicHeaderIterator(varyHeaders, "Vary")); when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(encHeaders); when(mockRequest.getHeaders("User-Agent")).thenReturn(uaHeaders); final String result = extractor.generateKey(defaultHost, mockRequest, mockEntry); verify(mockEntry).hasVariants(); verify(mockEntry).headerIterator("Vary"); verify(mockRequest).getHeaders("Accept-Encoding"); verify(mockRequest).getHeaders("User-Agent"); Assertions.assertEquals("{Accept-Encoding=gzip%2C+deflate&User-Agent=browser}" + theURI, result); } /* * "When comparing two URIs to decide if they match or not, a client * SHOULD use a case-sensitive octet-by-octet comparison of the entire * URIs, with these exceptions: * - A port that is empty or not given is equivalent to the default * port for that URI-reference; * - Comparisons of host names MUST be case-insensitive; * - Comparisons of scheme names MUST be case-insensitive; * - An empty abs_path is equivalent to an abs_path of "/". * Characters other than those in the 'reserved' and 'unsafe' sets * (see RFC 2396 [42]) are equivalent to their '"%" HEX HEX' encoding." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.3 */ @Test public void testEmptyPortEquivalentToDefaultPortForHttp() { final HttpHost host1 = new HttpHost("foo.example.com:"); final HttpHost host2 = new HttpHost("foo.example.com:80"); final HttpRequest req = new BasicHttpRequest("GET", "/"); Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req)); } @Test public void testEmptyPortEquivalentToDefaultPortForHttps() { final HttpHost host1 = new HttpHost("https", "foo.example.com", -1); final HttpHost host2 = new HttpHost("https", "foo.example.com", 443); final HttpRequest req = new BasicHttpRequest("GET", "/"); final String uri1 = extractor.generateKey(host1, req); final String uri2 = extractor.generateKey(host2, req); Assertions.assertEquals(uri1, uri2); } @Test public void testEmptyPortEquivalentToDefaultPortForHttpsAbsoluteURI() { final HttpHost host = new HttpHost("https", "foo.example.com", -1); final HttpGet get1 = new HttpGet("https://bar.example.com:/"); final HttpGet get2 = new HttpGet("https://bar.example.com:443/"); final String uri1 = extractor.generateKey(host, get1); final String uri2 = extractor.generateKey(host, get2); Assertions.assertEquals(uri1, uri2); } @Test public void testNotProvidedPortEquivalentToDefaultPortForHttpsAbsoluteURI() { final HttpHost host = new HttpHost("https", "foo.example.com", -1); final HttpGet get1 = new HttpGet("https://bar.example.com/"); final HttpGet get2 = new HttpGet("https://bar.example.com:443/"); final String uri1 = extractor.generateKey(host, get1); final String uri2 = extractor.generateKey(host, get2); Assertions.assertEquals(uri1, uri2); } @Test public void testNotProvidedPortEquivalentToDefaultPortForHttp() { final HttpHost host1 = new HttpHost("foo.example.com"); final HttpHost host2 = new HttpHost("foo.example.com:80"); final HttpRequest req = new BasicHttpRequest("GET", "/"); Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req)); } @Test public void testHostNameComparisonsAreCaseInsensitive() { final HttpHost host1 = new HttpHost("foo.example.com"); final HttpHost host2 = new HttpHost("FOO.EXAMPLE.COM"); final HttpRequest req = new BasicHttpRequest("GET", "/"); Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req)); } @Test public void testSchemeNameComparisonsAreCaseInsensitive() { final HttpHost host1 = new HttpHost("http", "foo.example.com", -1); final HttpHost host2 = new HttpHost("HTTP", "foo.example.com", -1); final HttpRequest req = new BasicHttpRequest("GET", "/"); Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req)); } @Test public void testEmptyAbsPathIsEquivalentToSlash() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/"); final HttpRequest req2 = new HttpGet("http://foo.example.com"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testExtraDotSegmentsAreIgnored() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/"); final HttpRequest req2 = new HttpGet("http://foo.example.com/./"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testExtraDotDotSegmentsAreIgnored() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/"); final HttpRequest req2 = new HttpGet("http://foo.example.com/.././../"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testIntermidateDotDotSegementsAreEquivalent() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html"); final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testIntermidateEncodedDotDotSegementsAreEquivalent() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html"); final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testIntermidateDotSegementsAreEquivalent() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html"); final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/./home.html"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testEquivalentPathEncodingsAreEquivalent() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html"); final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home.html"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testEquivalentExtraPathEncodingsAreEquivalent() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html"); final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home.html"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } @Test public void testEquivalentExtraPathEncodingsWithPercentAreEquivalent() { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home%20folder.html"); final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home%20folder.html"); Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2)); } } TestCacheRevalidatorBase.java000066400000000000000000000143561434266521000414040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.concurrent.RejectedExecutionException; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class TestCacheRevalidatorBase { @Mock private SchedulingStrategy mockSchedulingStrategy; @Mock private CacheRevalidatorBase.ScheduledExecutor mockScheduledExecutor; @Mock private Runnable mockOperation; private CacheRevalidatorBase impl; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); impl = new CacheRevalidatorBase(mockScheduledExecutor, mockSchedulingStrategy); } @Test public void testRevalidateCacheEntrySchedulesExecutionAndPopulatesIdentifier() { when(mockSchedulingStrategy.schedule(ArgumentMatchers.anyInt())).thenReturn(TimeValue.ofSeconds(1)); final String cacheKey = "blah"; impl.scheduleRevalidation(cacheKey, mockOperation); verify(mockSchedulingStrategy).schedule(0); verify(mockScheduledExecutor).schedule(ArgumentMatchers.same(mockOperation), ArgumentMatchers.eq(TimeValue.ofSeconds(1))); Assertions.assertEquals(1, impl.getScheduledIdentifiers().size()); } @Test public void testMarkCompleteRemovesIdentifier() { when(mockSchedulingStrategy.schedule(ArgumentMatchers.anyInt())).thenReturn(TimeValue.ofSeconds(3)); final String cacheKey = "blah"; impl.scheduleRevalidation(cacheKey, mockOperation); verify(mockSchedulingStrategy).schedule(0); verify(mockScheduledExecutor).schedule(ArgumentMatchers.any(), ArgumentMatchers.eq(TimeValue.ofSeconds(3))); Assertions.assertEquals(1, impl.getScheduledIdentifiers().size()); Assertions.assertTrue(impl.getScheduledIdentifiers().contains(cacheKey)); impl.jobSuccessful(cacheKey); Assertions.assertEquals(0, impl.getScheduledIdentifiers().size()); } @Test public void testRevalidateCacheEntryDoesNotPopulateIdentifierOnRejectedExecutionException() { when(mockSchedulingStrategy.schedule(ArgumentMatchers.anyInt())).thenReturn(TimeValue.ofSeconds(2)); doThrow(new RejectedExecutionException()).when(mockScheduledExecutor).schedule(ArgumentMatchers.any(), ArgumentMatchers.any()); final String cacheKey = "blah"; impl.scheduleRevalidation(cacheKey, mockOperation); Assertions.assertEquals(0, impl.getScheduledIdentifiers().size()); verify(mockScheduledExecutor).schedule(ArgumentMatchers.any(), ArgumentMatchers.eq(TimeValue.ofSeconds(2))); } @Test public void testRevalidateCacheEntryProperlyCollapsesRequest() { when(mockSchedulingStrategy.schedule(ArgumentMatchers.anyInt())).thenReturn(TimeValue.ofSeconds(2)); final String cacheKey = "blah"; impl.scheduleRevalidation(cacheKey, mockOperation); impl.scheduleRevalidation(cacheKey, mockOperation); impl.scheduleRevalidation(cacheKey, mockOperation); verify(mockSchedulingStrategy).schedule(ArgumentMatchers.anyInt()); verify(mockScheduledExecutor).schedule(ArgumentMatchers.any(), ArgumentMatchers.eq(TimeValue.ofSeconds(2))); Assertions.assertEquals(1, impl.getScheduledIdentifiers().size()); } @Test public void testStaleResponse() { final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_OK); response1.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\""); assertThat(impl.isStale(response1), CoreMatchers.equalTo(true)); final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_OK); response2.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\""); assertThat(impl.isStale(response2), CoreMatchers.equalTo(true)); final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_OK); response3.addHeader(HeaderConstants.WARNING, "xxx localhost \"Huh?\""); assertThat(impl.isStale(response3), CoreMatchers.equalTo(false)); final HttpResponse response4 = new BasicHttpResponse(HttpStatus.SC_OK); assertThat(impl.isStale(response4), CoreMatchers.equalTo(false)); } @Test public void testShutdown() throws Exception { impl.close(); impl.awaitTermination(Timeout.ofMinutes(2)); verify(mockScheduledExecutor).shutdown(); verify(mockScheduledExecutor).awaitTermination(Timeout.ofMinutes(2)); } } TestCacheUpdateHandler.java000066400000000000000000000330701434266521000410470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.time.Instant; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestCacheUpdateHandler { private Instant requestDate; private Instant responseDate; private CacheUpdateHandler impl; private HttpCacheEntry entry; private Instant now; private Instant oneSecondAgo; private Instant twoSecondsAgo; private Instant eightSecondsAgo; private Instant tenSecondsAgo; private HttpResponse response; @BeforeEach public void setUp() throws Exception { requestDate = Instant.now().minusSeconds(1); responseDate = Instant.now(); now = Instant.now(); oneSecondAgo = now.minusSeconds(1); twoSecondsAgo = now.minusSeconds(2); eightSecondsAgo = now.minusSeconds(8); tenSecondsAgo = now.minusSeconds(10); response = new BasicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); impl = new CacheUpdateHandler(); } @Test public void testUpdateCacheEntryReturnsDifferentEntryInstance() throws IOException { entry = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry newEntry = impl.updateCacheEntry(null, entry, requestDate, responseDate, response); assertNotSame(newEntry, entry); } @Test public void testHeadersAreMergedCorrectly() throws IOException { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(responseDate)), new BasicHeader("ETag", "\"etag\"")}; entry = HttpTestUtils.makeCacheEntry(headers); response.setHeaders(); final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry, Instant.now(), Instant.now(), response); assertThat(updatedEntry, ContainsHeaderMatcher.contains("Date", DateUtils.formatStandardDate(responseDate))); assertThat(updatedEntry, ContainsHeaderMatcher.contains("ETag", "\"etag\"")); } @Test public void testNewerHeadersReplaceExistingHeaders() throws IOException { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(requestDate)), new BasicHeader("Cache-Control", "private"), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(requestDate)), new BasicHeader("Cache-Control", "max-age=0"),}; entry = HttpTestUtils.makeCacheEntry(headers); response.setHeaders(new BasicHeader("Last-Modified", DateUtils.formatStandardDate(responseDate)), new BasicHeader("Cache-Control", "public")); final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry, Instant.now(), Instant.now(), response); assertThat(updatedEntry, ContainsHeaderMatcher.contains("Date", DateUtils.formatStandardDate(requestDate))); assertThat(updatedEntry, ContainsHeaderMatcher.contains("ETag", "\"etag\"")); assertThat(updatedEntry, ContainsHeaderMatcher.contains("Last-Modified", DateUtils.formatStandardDate(responseDate))); assertThat(updatedEntry, ContainsHeaderMatcher.contains("Cache-Control", "public")); } @Test public void testNewHeadersAreAddedByMerge() throws IOException { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(requestDate)), new BasicHeader("ETag", "\"etag\"")}; entry = HttpTestUtils.makeCacheEntry(headers); response.setHeaders(new BasicHeader("Last-Modified", DateUtils.formatStandardDate(responseDate)), new BasicHeader("Cache-Control", "public")); final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry, Instant.now(), Instant.now(), response); assertThat(updatedEntry, ContainsHeaderMatcher.contains("Date", DateUtils.formatStandardDate(requestDate))); assertThat(updatedEntry, ContainsHeaderMatcher.contains("ETag", "\"etag\"")); assertThat(updatedEntry, ContainsHeaderMatcher.contains("Last-Modified", DateUtils.formatStandardDate(responseDate))); assertThat(updatedEntry, ContainsHeaderMatcher.contains("Cache-Control", "public")); } @Test public void oldHeadersRetainedIfResponseOlderThanEntry() throws Exception { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)), new BasicHeader("ETag", "\"new-etag\"") }; entry = HttpTestUtils.makeCacheEntry(twoSecondsAgo, now, headers); response.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); response.setHeader("ETag", "\"old-etag\""); final HttpCacheEntry result = impl.updateCacheEntry("A", entry, Instant.now(), Instant.now(), response); assertThat(result, ContainsHeaderMatcher.contains("Date", DateUtils.formatStandardDate(oneSecondAgo))); assertThat(result, ContainsHeaderMatcher.contains("ETag", "\"new-etag\"")); } @Test public void testUpdatedEntryHasLatestRequestAndResponseDates() throws IOException { entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo); final HttpCacheEntry updated = impl.updateCacheEntry(null, entry, twoSecondsAgo, oneSecondAgo, response); assertEquals(twoSecondsAgo, updated.getRequestInstant()); assertEquals(oneSecondAgo, updated.getResponseInstant()); } @Test public void entry1xxWarningsAreRemovedOnUpdate() throws Exception { final Header[] headers = { new BasicHeader("Warning", "110 fred \"Response is stale\""), new BasicHeader("ETag", "\"old\""), new BasicHeader("Date", DateUtils.formatStandardDate(eightSecondsAgo)) }; entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, headers); response.setHeader("ETag", "\"new\""); response.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); final HttpCacheEntry updated = impl.updateCacheEntry(null, entry, twoSecondsAgo, oneSecondAgo, response); assertEquals(0, updated.getHeaders("Warning").length); } @Test public void entryWithMalformedDateIsStillUpdated() throws Exception { final Header[] headers = { new BasicHeader("ETag", "\"old\""), new BasicHeader("Date", "bad-date") }; entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, headers); response.setHeader("ETag", "\"new\""); response.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); final HttpCacheEntry updated = impl.updateCacheEntry(null, entry, twoSecondsAgo, oneSecondAgo, response); assertEquals("\"new\"", updated.getFirstHeader("ETag").getValue()); } @Test public void entryIsStillUpdatedByResponseWithMalformedDate() throws Exception { final Header[] headers = { new BasicHeader("ETag", "\"old\""), new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)) }; entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, headers); response.setHeader("ETag", "\"new\""); response.setHeader("Date", "bad-date"); final HttpCacheEntry updated = impl.updateCacheEntry(null, entry, twoSecondsAgo, oneSecondAgo, response); assertEquals("\"new\"", updated.getFirstHeader("ETag").getValue()); } @Test public void cannotUpdateFromANon304OriginResponse() throws Exception { entry = HttpTestUtils.makeCacheEntry(); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); try { impl.updateCacheEntry("A", entry, Instant.now(), Instant.now(), response); fail("should have thrown exception"); } catch (final IllegalArgumentException expected) { } } @Test public void testCacheUpdateAddsVariantURIToParentEntry() throws Exception { final String parentCacheKey = "parentCacheKey"; final String variantCacheKey = "variantCacheKey"; final String existingVariantKey = "existingVariantKey"; final String newVariantCacheKey = "newVariantCacheKey"; final String newVariantKey = "newVariantKey"; final Map existingVariants = new HashMap<>(); existingVariants.put(existingVariantKey, variantCacheKey); final HttpCacheEntry parent = HttpTestUtils.makeCacheEntry(existingVariants); final HttpCacheEntry variant = HttpTestUtils.makeCacheEntry(); final HttpCacheEntry result = impl.updateParentCacheEntry(parentCacheKey, parent, variant, newVariantKey, newVariantCacheKey); final Map resultMap = result.getVariantMap(); assertEquals(2, resultMap.size()); assertEquals(variantCacheKey, resultMap.get(existingVariantKey)); assertEquals(newVariantCacheKey, resultMap.get(newVariantKey)); } @Test public void testContentEncodingHeaderIsNotUpdatedByMerge() throws IOException { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(requestDate)), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Content-Encoding", "identity")}; entry = HttpTestUtils.makeCacheEntry(headers); response.setHeaders(new BasicHeader("Last-Modified", DateUtils.formatStandardDate(responseDate)), new BasicHeader("Cache-Control", "public"), new BasicHeader("Content-Encoding", "gzip")); final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry, Instant.now(), Instant.now(), response); final Header[] updatedHeaders = updatedEntry.getHeaders(); headersContain(updatedHeaders, "Content-Encoding", "identity"); headersNotContain(updatedHeaders, "Content-Encoding", "gzip"); } @Test public void testContentLengthIsNotAddedWhenTransferEncodingIsPresent() throws IOException { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(requestDate)), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Transfer-Encoding", "chunked")}; entry = HttpTestUtils.makeCacheEntry(headers); response.setHeaders(new BasicHeader("Last-Modified", DateUtils.formatStandardDate(responseDate)), new BasicHeader("Cache-Control", "public"), new BasicHeader("Content-Length", "0")); final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry, Instant.now(), Instant.now(), response); final Header[] updatedHeaders = updatedEntry.getHeaders(); headersContain(updatedHeaders, "Transfer-Encoding", "chunked"); headersNotContain(updatedHeaders, "Content-Length", "0"); } private void headersContain(final Header[] headers, final String name, final String value) { for (final Header header : headers) { if (header.getName().equals(name)) { if (header.getValue().equals(value)) { return; } } } fail("Header [" + name + ": " + value + "] not found in headers."); } private void headersNotContain(final Header[] headers, final String name, final String value) { for (final Header header : headers) { if (header.getName().equals(name)) { if (header.getValue().equals(value)) { fail("Header [" + name + ": " + value + "] found in headers where it should not be"); } } } } } TestCacheValidityPolicy.java000066400000000000000000000530741434266521000413020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Instant; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestCacheValidityPolicy { private CacheValidityPolicy impl; private Instant now; private Instant oneSecondAgo; private Instant sixSecondsAgo; private Instant tenSecondsAgo; private Instant elevenSecondsAgo; @BeforeEach public void setUp() { impl = new CacheValidityPolicy(); now = Instant.now(); oneSecondAgo = now.minusSeconds(1); sixSecondsAgo = now.minusSeconds(6); tenSecondsAgo = now.minusSeconds(10); elevenSecondsAgo = now.minusSeconds(11); } @Test public void testApparentAgeIsMaxIntIfDateHeaderNotPresent() { final Header[] headers = { new BasicHeader("Server", "MockServer/1.0") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(CacheValidityPolicy.MAX_AGE, impl.getApparentAge(entry)); } @Test public void testApparentAgeIsResponseReceivedTimeLessDateHeader() { final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo, headers); assertEquals(TimeValue.ofSeconds(4), impl.getApparentAge(entry)); } @Test public void testNegativeApparentAgeIsBroughtUpToZero() { final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, tenSecondsAgo, headers); assertEquals(TimeValue.ofSeconds(0), impl.getApparentAge(entry)); } @Test public void testCorrectedReceivedAgeIsAgeHeaderIfLarger() { final Header[] headers = new Header[] { new BasicHeader("Age", "10"), }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); impl = new CacheValidityPolicy() { @Override protected TimeValue getApparentAge(final HttpCacheEntry ent) { return TimeValue.ofSeconds(6); } }; assertEquals(TimeValue.ofSeconds(10), impl.getCorrectedReceivedAge(entry)); } @Test public void testCorrectedReceivedAgeIsApparentAgeIfLarger() { final Header[] headers = new Header[] { new BasicHeader("Age", "6"), }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); impl = new CacheValidityPolicy() { @Override protected TimeValue getApparentAge(final HttpCacheEntry ent) { return TimeValue.ofSeconds(10); } }; assertEquals(TimeValue.ofSeconds(10), impl.getCorrectedReceivedAge(entry)); } @Test public void testResponseDelayIsDifferenceBetweenResponseAndRequestTimes() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, sixSecondsAgo); assertEquals(TimeValue.ofSeconds(4), impl.getResponseDelay(entry)); } @Test public void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); impl = new CacheValidityPolicy() { @Override protected TimeValue getCorrectedReceivedAge(final HttpCacheEntry ent) { return TimeValue.ofSeconds(7); } @Override protected TimeValue getResponseDelay(final HttpCacheEntry ent) { return TimeValue.ofSeconds(13); } }; assertEquals(TimeValue.ofSeconds(20), impl.getCorrectedInitialAge(entry)); } @Test public void testResidentTimeSecondsIsTimeSinceResponseTime() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo); assertEquals(TimeValue.ofSeconds(6), impl.getResidentTime(entry, now)); } @Test public void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); impl = new CacheValidityPolicy() { @Override protected TimeValue getCorrectedInitialAge(final HttpCacheEntry ent) { return TimeValue.ofSeconds(11); } @Override protected TimeValue getResidentTime(final HttpCacheEntry ent, final Instant d) { return TimeValue.ofSeconds(17); } }; assertEquals(TimeValue.ofSeconds(28), impl.getCurrentAge(entry, Instant.now())); } @Test public void testFreshnessLifetimeIsSMaxAgeIfPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(entry)); } @Test public void testFreshnessLifetimeIsMaxAgeIfPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(entry)); } @Test public void testFreshnessLifetimeIsMostRestrictiveOfMaxAgeAndSMaxAge() { Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"), new BasicHeader("Cache-Control", "s-maxage=20") }; HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(entry)); headers = new Header[] { new BasicHeader("Cache-Control", "max-age=20"), new BasicHeader("Cache-Control", "s-maxage=10") }; entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(entry)); } @Test public void testFreshnessLifetimeIsMaxAgeEvenIfExpiresIsPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"), new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(entry)); } @Test public void testFreshnessLifetimeIsSMaxAgeEvenIfExpiresIsPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10"), new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(entry)); } @Test public void testFreshnessLifetimeIsFromExpiresHeaderIfNoMaxAge() { final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(4), impl.getFreshnessLifetime(entry)); } @Test public void testHeuristicFreshnessLifetime() { final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(elevenSecondsAgo)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertEquals(TimeValue.ofSeconds(1), impl.getHeuristicFreshnessLifetime(entry, 0.1f, TimeValue.ZERO_MILLISECONDS)); } @Test public void testHeuristicFreshnessLifetimeDefaultsProperly() { final TimeValue defaultFreshness = TimeValue.ofSeconds(10); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetime(entry, 0.1f, defaultFreshness)); } @Test public void testHeuristicFreshnessLifetimeIsNonNegative() { final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(elevenSecondsAgo)), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(oneSecondAgo)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertTrue(TimeValue.isNonNegative(impl.getHeuristicFreshnessLifetime(entry, 0.1f, TimeValue.ofSeconds(10)))); } @Test public void testResponseIsFreshIfFreshnessLifetimeExceedsCurrentAge() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); impl = new CacheValidityPolicy() { @Override public TimeValue getCurrentAge(final HttpCacheEntry e, final Instant d) { assertSame(entry, e); assertEquals(now, d); return TimeValue.ofSeconds(6); } @Override public TimeValue getFreshnessLifetime(final HttpCacheEntry e) { assertSame(entry, e); return TimeValue.ofSeconds(10); } }; assertTrue(impl.isResponseFresh(entry, now)); } @Test public void testResponseIsNotFreshIfFreshnessLifetimeEqualsCurrentAge() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); impl = new CacheValidityPolicy() { @Override public TimeValue getCurrentAge(final HttpCacheEntry e, final Instant d) { assertEquals(now, d); assertSame(entry, e); return TimeValue.ofSeconds(6); } @Override public TimeValue getFreshnessLifetime(final HttpCacheEntry e) { assertSame(entry, e); return TimeValue.ofSeconds(6); } }; assertFalse(impl.isResponseFresh(entry, now)); } @Test public void testResponseIsNotFreshIfCurrentAgeExceedsFreshnessLifetime() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); impl = new CacheValidityPolicy() { @Override public TimeValue getCurrentAge(final HttpCacheEntry e, final Instant d) { assertEquals(now, d); assertSame(entry, e); return TimeValue.ofSeconds(10); } @Override public TimeValue getFreshnessLifetime(final HttpCacheEntry e) { assertSame(entry, e); return TimeValue.ofSeconds(6); } }; assertFalse(impl.isResponseFresh(entry, now)); } @Test public void testCacheEntryIsRevalidatableIfHeadersIncludeETag() { final Header[] headers = { new BasicHeader("Expires", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("ETag", "somevalue")}; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertTrue(impl.isRevalidatable(entry)); } @Test public void testCacheEntryIsRevalidatableIfHeadersIncludeLastModifiedDate() { final Header[] headers = { new BasicHeader("Expires", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now())) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertTrue(impl.isRevalidatable(entry)); } @Test public void testCacheEntryIsNotRevalidatableIfNoAppropriateHeaders() { final Header[] headers = { new BasicHeader("Expires", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("Cache-Control", "public") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertFalse(impl.isRevalidatable(entry)); } @Test public void testMissingContentLengthDoesntInvalidateEntry() { final int contentLength = 128; final Header[] headers = {}; // no Content-Length header final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength)); assertTrue(impl.contentLengthHeaderMatchesActualLength(entry)); } @Test public void testCorrectContentLengthDoesntInvalidateEntry() { final int contentLength = 128; final Header[] headers = { new BasicHeader(HttpHeaders.CONTENT_LENGTH, Integer.toString(contentLength)) }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength)); assertTrue(impl.contentLengthHeaderMatchesActualLength(entry)); } @Test public void testWrongContentLengthInvalidatesEntry() { final int contentLength = 128; final Header[] headers = {new BasicHeader(HttpHeaders.CONTENT_LENGTH, Integer.toString(contentLength+1))}; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength)); assertFalse(impl.contentLengthHeaderMatchesActualLength(entry)); } @Test public void testNullResourceInvalidatesEntry() { final int contentLength = 128; final Header[] headers = {new BasicHeader(HttpHeaders.CONTENT_LENGTH, Integer.toString(contentLength))}; final HttpCacheEntry entry = HttpTestUtils.makeHeadCacheEntry(headers); assertFalse(impl.contentLengthHeaderMatchesActualLength(entry)); } @Test public void testNegativeAgeHeaderValueReturnsMaxAge() { final Header[] headers = new Header[] { new BasicHeader("Age", "-100") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); // in seconds assertEquals(CacheValidityPolicy.MAX_AGE.toSeconds(), impl.getAgeValue(entry)); } @Test public void testMalformedAgeHeaderValueReturnsMaxAge() { final Header[] headers = new Header[] { new BasicHeader("Age", "asdf") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); // in seconds assertEquals(CacheValidityPolicy.MAX_AGE.toSeconds(), impl.getAgeValue(entry)); } @Test public void testMalformedCacheControlMaxAgeHeaderReturnsZero() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=asdf") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); // in seconds assertEquals(0L, impl.getMaxAge(entry)); } @Test public void testMustRevalidateIsFalseIfDirectiveNotPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertFalse(impl.mustRevalidate(entry)); } @Test public void testMustRevalidateIsTrueWhenDirectiveIsPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, must-revalidate") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertTrue(impl.mustRevalidate(entry)); } @Test public void testProxyRevalidateIsFalseIfDirectiveNotPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertFalse(impl.proxyRevalidate(entry)); } @Test public void testProxyRevalidateIsTrueWhenDirectiveIsPresent() { final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public, proxy-revalidate") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertTrue(impl.proxyRevalidate(entry)); } @Test public void testMayReturnStaleIfErrorInResponseIsTrueWithinStaleness(){ final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5, stale-if-error=15") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); final HttpRequest req = new BasicHttpRequest("GET","/"); assertTrue(impl.mayReturnStaleIfError(req, entry, now)); } @Test public void testMayReturnStaleIfErrorInRequestIsTrueWithinStaleness(){ final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); final HttpRequest req = new BasicHttpRequest("GET","/"); req.setHeader("Cache-Control","stale-if-error=15"); assertTrue(impl.mayReturnStaleIfError(req, entry, now)); } @Test public void testMayNotReturnStaleIfErrorInResponseAndAfterResponseWindow(){ final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5, stale-if-error=1") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); final HttpRequest req = new BasicHttpRequest("GET","/"); assertFalse(impl.mayReturnStaleIfError(req, entry, now)); } @Test public void testMayNotReturnStaleIfErrorInResponseAndAfterRequestWindow(){ final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); final HttpRequest req = new BasicHttpRequest("GET","/"); req.setHeader("Cache-Control","stale-if-error=1"); assertFalse(impl.mayReturnStaleIfError(req, entry, now)); } @Test public void testMayReturnStaleWhileRevalidatingIsFalseWhenDirectiveIsAbsent() { final Header[] headers = new Header[] { new BasicHeader("Cache-control", "public") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers); assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now)); } @Test public void testMayReturnStaleWhileRevalidatingIsTrueWhenWithinStaleness() { final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=15") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); assertTrue(impl.mayReturnStaleWhileRevalidating(entry, now)); } @Test public void testMayReturnStaleWhileRevalidatingIsFalseWhenPastStaleness() { final Instant twentyFiveSecondsAgo = now.minusSeconds(25); final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(twentyFiveSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=15") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now)); } @Test public void testMayReturnStaleWhileRevalidatingIsFalseWhenDirectiveEmpty() { final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now)); } } TestCacheableRequestPolicy.java000066400000000000000000000120571434266521000417650ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestCacheableRequestPolicy { private CacheableRequestPolicy policy; @BeforeEach public void setUp() throws Exception { policy = new CacheableRequestPolicy(); } @Test public void testIsGetServableFromCache() { final BasicHttpRequest request = new BasicHttpRequest("GET", "someUri"); Assertions.assertTrue(policy.isServableFromCache(request)); } @Test public void testIsGetWithCacheControlServableFromCache() { BasicHttpRequest request = new BasicHttpRequest("GET", "someUri"); request.addHeader("Cache-Control", "no-cache"); Assertions.assertFalse(policy.isServableFromCache(request)); request = new BasicHttpRequest("GET", "someUri"); request.addHeader("Cache-Control", "no-store"); request.addHeader("Cache-Control", "max-age=20"); Assertions.assertFalse(policy.isServableFromCache(request)); request = new BasicHttpRequest("GET", "someUri"); request.addHeader("Cache-Control", "public"); request.addHeader("Cache-Control", "no-store, max-age=20"); Assertions.assertFalse(policy.isServableFromCache(request)); } @Test public void testIsGetWithPragmaServableFromCache() { BasicHttpRequest request = new BasicHttpRequest("GET", "someUri"); request.addHeader("Pragma", "no-cache"); Assertions.assertFalse(policy.isServableFromCache(request)); request = new BasicHttpRequest("GET", "someUri"); request.addHeader("Pragma", "value1"); request.addHeader("Pragma", "value2"); Assertions.assertFalse(policy.isServableFromCache(request)); } @Test public void testIsHeadServableFromCache() { BasicHttpRequest request = new BasicHttpRequest("HEAD", "someUri"); Assertions.assertTrue(policy.isServableFromCache(request)); request = new BasicHttpRequest("HEAD", "someUri"); request.addHeader("Cache-Control", "public"); request.addHeader("Cache-Control", "max-age=20"); Assertions.assertTrue(policy.isServableFromCache(request)); } @Test public void testIsHeadWithCacheControlServableFromCache() { BasicHttpRequest request = new BasicHttpRequest("HEAD", "someUri"); request.addHeader("Cache-Control", "no-cache"); Assertions.assertFalse(policy.isServableFromCache(request)); request = new BasicHttpRequest("HEAD", "someUri"); request.addHeader("Cache-Control", "no-store"); request.addHeader("Cache-Control", "max-age=20"); Assertions.assertFalse(policy.isServableFromCache(request)); request = new BasicHttpRequest("HEAD", "someUri"); request.addHeader("Cache-Control", "public"); request.addHeader("Cache-Control", "no-store, max-age=20"); Assertions.assertFalse(policy.isServableFromCache(request)); } @Test public void testIsHeadWithPragmaServableFromCache() { BasicHttpRequest request = new BasicHttpRequest("HEAD", "someUri"); request.addHeader("Pragma", "no-cache"); Assertions.assertFalse(policy.isServableFromCache(request)); request = new BasicHttpRequest("HEAD", "someUri"); request.addHeader("Pragma", "value1"); request.addHeader("Pragma", "value2"); Assertions.assertFalse(policy.isServableFromCache(request)); } @Test public void testIsArbitraryMethodServableFromCache() { BasicHttpRequest request = new BasicHttpRequest("TRACE", "someUri"); Assertions.assertFalse(policy.isServableFromCache(request)); request = new BasicHttpRequest("get", "someUri"); Assertions.assertFalse(policy.isServableFromCache(request)); } } TestCachedHttpResponseGenerator.java000066400000000000000000000150071434266521000430000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.time.Instant; import java.util.HashMap; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @SuppressWarnings({"boxing","static-access"}) // test code public class TestCachedHttpResponseGenerator { private HttpCacheEntry entry; private ClassicHttpRequest request; private CacheValidityPolicy mockValidityPolicy; private CachedHttpResponseGenerator impl; @BeforeEach public void setUp() { entry = HttpTestUtils.makeCacheEntry(new HashMap<>()); request = HttpTestUtils.makeDefaultRequest(); mockValidityPolicy = mock(CacheValidityPolicy.class); impl = new CachedHttpResponseGenerator(mockValidityPolicy); } @Test public void testResponseHasContentLength() throws Exception { final byte[] buf = new byte[] { 1, 2, 3, 4, 5 }; final HttpCacheEntry entry1 = HttpTestUtils.makeCacheEntry(buf); final SimpleHttpResponse response = impl.generateResponse(request, entry1); final Header length = response.getFirstHeader("Content-Length"); Assertions.assertNotNull(length, "Content-Length Header is missing"); Assertions.assertEquals(buf.length, Integer.parseInt(length.getValue()), "Content-Length does not match buffer length"); } @Test public void testContentLengthIsNotAddedWhenTransferEncodingIsPresent() throws Exception { final Header[] hdrs = new Header[] { new BasicHeader("Transfer-Encoding", "chunked") }; final byte[] buf = new byte[] { 1, 2, 3, 4, 5 }; final HttpCacheEntry entry1 = HttpTestUtils.makeCacheEntry(hdrs, buf); final SimpleHttpResponse response = impl.generateResponse(request, entry1); final Header length = response.getFirstHeader("Content-Length"); Assertions.assertNull(length); } @Test public void testResponseMatchesCacheEntry() throws Exception { final SimpleHttpResponse response = impl.generateResponse(request, entry); Assertions.assertTrue(response.containsHeader("Content-Length")); Assertions.assertSame("HTTP", response.getVersion().getProtocol()); Assertions.assertEquals(1, response.getVersion().getMajor()); Assertions.assertEquals(1, response.getVersion().getMinor()); } @Test public void testResponseStatusCodeMatchesCacheEntry() throws Exception { final SimpleHttpResponse response = impl.generateResponse(request, entry); Assertions.assertEquals(entry.getStatus(), response.getCode()); } @Test public void testAgeHeaderIsPopulatedWithCurrentAgeOfCacheEntryIfNonZero() throws Exception { currentAge(TimeValue.ofSeconds(10L)); final SimpleHttpResponse response = impl.generateResponse(request, entry); verify(mockValidityPolicy).getCurrentAge(same(entry), isA(Instant.class)); final Header ageHdr = response.getFirstHeader("Age"); Assertions.assertNotNull(ageHdr); Assertions.assertEquals(10L, Long.parseLong(ageHdr.getValue())); } @Test public void testAgeHeaderIsNotPopulatedIfCurrentAgeOfCacheEntryIsZero() throws Exception { currentAge(TimeValue.ofSeconds(0L)); final SimpleHttpResponse response = impl.generateResponse(request, entry); verify(mockValidityPolicy).getCurrentAge(same(entry), isA(Instant.class)); final Header ageHdr = response.getFirstHeader("Age"); Assertions.assertNull(ageHdr); } @Test public void testAgeHeaderIsPopulatedWithMaxAgeIfCurrentAgeTooBig() throws Exception { currentAge(TimeValue.ofSeconds(CacheValidityPolicy.MAX_AGE.toSeconds() + 1L)); final SimpleHttpResponse response = impl.generateResponse(request, entry); verify(mockValidityPolicy).getCurrentAge(same(entry), isA(Instant.class)); final Header ageHdr = response.getFirstHeader("Age"); Assertions.assertNotNull(ageHdr); Assertions.assertEquals(CacheValidityPolicy.MAX_AGE.toSeconds(), Long.parseLong(ageHdr.getValue())); } private void currentAge(final TimeValue age) { when( mockValidityPolicy.getCurrentAge(same(entry), isA(Instant.class))).thenReturn(age); } @Test public void testResponseContainsEntityToServeGETRequestIfEntryContainsResource() throws Exception { final SimpleHttpResponse response = impl.generateResponse(request, entry); Assertions.assertNotNull(response.getBody()); } @Test public void testResponseDoesNotContainEntityToServeHEADRequestIfEntryContainsResource() throws Exception { final ClassicHttpRequest headRequest = HttpTestUtils.makeDefaultHEADRequest(); final SimpleHttpResponse response = impl.generateResponse(headRequest, entry); Assertions.assertNull(response.getBody()); } } TestCachedResponseSuitabilityChecker.java000066400000000000000000000331511434266521000440010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestCachedResponseSuitabilityChecker { private Instant now; private Instant elevenSecondsAgo; private Instant tenSecondsAgo; private Instant nineSecondsAgo; private HttpHost host; private HttpRequest request; private HttpCacheEntry entry; private CachedResponseSuitabilityChecker impl; @BeforeEach public void setUp() { now = Instant.now(); elevenSecondsAgo = now.minusSeconds(11); tenSecondsAgo = now.minusSeconds(10); nineSecondsAgo = now.minusSeconds(9); host = new HttpHost("foo.example.com"); request = new BasicHttpRequest("GET", "/foo"); entry = HttpTestUtils.makeCacheEntry(); impl = new CachedResponseSuitabilityChecker(CacheConfig.DEFAULT); } private HttpCacheEntry getEntry(final Header[] headers) { return HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, headers); } @Test public void testNotSuitableIfContentLengthHeaderIsWrong() { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","1") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableIfCacheEntryIsFresh() { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testNotSuitableIfCacheEntryIsNotFresh() { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testNotSuitableIfRequestHasNoCache() { request.addHeader("Cache-Control", "no-cache"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testNotSuitableIfAgeExceedsRequestMaxAge() { request.addHeader("Cache-Control", "max-age=10"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableIfFreshAndAgeIsUnderRequestMaxAge() { request.addHeader("Cache-Control", "max-age=15"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableIfFreshAndFreshnessLifetimeGreaterThanRequestMinFresh() { request.addHeader("Cache-Control", "min-fresh=10"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testNotSuitableIfFreshnessLifetimeLessThanRequestMinFresh() { request.addHeader("Cache-Control", "min-fresh=10"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=15"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableEvenIfStaleButPermittedByRequestMaxStale() { request.addHeader("Cache-Control", "max-stale=10"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testNotSuitableIfStaleButTooStaleForRequestMaxStale() { request.addHeader("Cache-Control", "max-stale=2"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testMalformedCacheControlMaxAgeRequestHeaderCausesUnsuitableEntry() { request.addHeader(new BasicHeader("Cache-Control", "max-age=foo")); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testMalformedCacheControlMinFreshRequestHeaderCausesUnsuitableEntry() { request.addHeader(new BasicHeader("Cache-Control", "min-fresh=foo")); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableIfCacheEntryIsHeuristicallyFreshEnough() { final Instant oneSecondAgo = now.minusSeconds(1); final Instant twentyOneSecondsAgo = now.minusSeconds(21); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(twentyOneSecondsAgo)), new BasicHeader("Content-Length", "128") }; entry = HttpTestUtils.makeCacheEntry(oneSecondAgo, oneSecondAgo, headers); final CacheConfig config = CacheConfig.custom() .setHeuristicCachingEnabled(true) .setHeuristicCoefficient(0.1f).build(); impl = new CachedResponseSuitabilityChecker(config); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableIfCacheEntryIsHeuristicallyFreshEnoughByDefault() { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Content-Length", "128") }; entry = getEntry(headers); final CacheConfig config = CacheConfig.custom() .setHeuristicCachingEnabled(true) .setHeuristicDefaultLifetime(TimeValue.ofSeconds(20L)) .build(); impl = new CachedResponseSuitabilityChecker(config); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableIfRequestMethodisHEAD() { final HttpRequest headRequest = new BasicHttpRequest("HEAD", "/foo"); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = getEntry(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, headRequest, entry, now)); } @Test public void testNotSuitableIfRequestMethodIsGETAndEntryResourceIsNull() { final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = HttpTestUtils.makeHeadCacheEntry(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testNotSuitableForGETIfEntryDoesNotSpecifyARequestMethodOrEntity() { impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build()); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = HttpTestUtils.makeCacheEntryWithNoRequestMethodOrEntity(headers); Assertions.assertFalse(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableForGETIfEntryDoesNotSpecifyARequestMethodButContainsEntity() { impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build()); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = HttpTestUtils.makeCacheEntryWithNoRequestMethod(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableForGETIfHeadResponseCachingEnabledAndEntryDoesNotSpecifyARequestMethodButContains204Response() { impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build()); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600") }; entry = HttpTestUtils.make204CacheEntryWithNoRequestMethod(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now)); } @Test public void testSuitableForHEADIfHeadResponseCachingEnabledAndEntryDoesNotSpecifyARequestMethod() { final HttpRequest headRequest = new BasicHttpRequest("HEAD", "/foo"); impl = new CachedResponseSuitabilityChecker(CacheConfig.custom().build()); final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length","128") }; entry = HttpTestUtils.makeHeadCacheEntryWithNoRequestMethod(headers); Assertions.assertTrue(impl.canCachedResponseBeUsed(host, headRequest, entry, now)); } } TestCachingExecChain.java000066400000000000000000001652751434266521000405240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.junit.jupiter.api.Assertions.assertSame; import java.io.IOException; import java.io.InputStream; import java.net.SocketException; import java.net.SocketTimeoutException; import java.time.Instant; import java.time.temporal.ChronoUnit; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.cache.CacheResponseStatus; import org.apache.hc.client5.http.cache.HttpCacheContext; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpOptions; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.net.URIAuthority; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; public class TestCachingExecChain { @Mock ExecChain mockExecChain; @Mock ExecRuntime mockExecRuntime; @Mock HttpCacheStorage mockStorage; @Spy HttpCache cache = new BasicHttpCache(); CacheConfig config; HttpRoute route; HttpHost host; ClassicHttpRequest request; HttpCacheContext context; HttpCacheEntry entry; CachingExec impl; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); config = CacheConfig.DEFAULT; host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); request = new BasicClassicHttpRequest("GET", "/stuff"); context = HttpCacheContext.create(); entry = HttpTestUtils.makeCacheEntry(); impl = new CachingExec(cache, null, CacheConfig.DEFAULT); } public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException { final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context); return impl.execute(ClassicRequestBuilder.copy(request).build(), scope, mockExecChain); } @Test public void testCacheableResponsesGoIntoCache() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "max-age=3600"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); execute(req2); Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any()); Mockito.verify(cache).createCacheEntry(Mockito.eq(host), RequestEquivalent.eq(req1), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testOlderCacheableResponsesDoNotGoIntoCache() throws Exception { final Instant now = Instant.now(); final Instant fiveSecondsAgo = now.minusSeconds(5); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Etag", "\"new-etag\""); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "no-cache"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag", "\"old-etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(fiveSecondsAgo)); resp2.setHeader("Cache-Control", "max-age=3600"); final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ClassicHttpResponse result = execute(req3); Assertions.assertEquals("\"new-etag\"", result.getFirstHeader("ETag").getValue()); } @Test public void testNewerCacheableResponsesReplaceExistingCacheEntry() throws Exception { final Instant now = Instant.now(); final Instant fiveSecondsAgo = now.minusSeconds(5); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(fiveSecondsAgo)); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Etag", "\"old-etag\""); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "max-age=0"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag", "\"new-etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Cache-Control", "max-age=3600"); final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ClassicHttpResponse result = execute(req3); Assertions.assertEquals("\"new-etag\"", result.getFirstHeader("ETag").getValue()); } @Test public void testNonCacheableResponseIsNotCachedAndIsReturnedAsIs() throws Exception { final HttpCache cache = new BasicHttpCache(new HeapResourceFactory(), mockStorage); impl = new CachingExec(cache, null, CacheConfig.DEFAULT); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "no-cache"); Mockito.when(mockStorage.getEntry(Mockito.any())).thenReturn(null); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result = execute(req1); Assertions.assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result)); Mockito.verify(mockStorage, Mockito.never()).putEntry(Mockito.any(), Mockito.any()); } @Test public void testSetsModuleGeneratedResponseContextForCacheOptionsResponse() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("OPTIONS", "*"); req.setHeader("Max-Forwards", "0"); execute(req); Assertions.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus()); } @Test public void testSetsModuleGeneratedResponseContextForFatallyNoncompliantRequest() throws Exception { final ClassicHttpRequest req = new HttpGet("http://foo.example.com/"); req.setHeader("Range", "bytes=0-50"); req.setHeader("If-Range", "W/\"weak-etag\""); execute(req); Assertions.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus()); } @Test public void testRecordsClientProtocolInViaHeaderIfRequestNotServableFromCache() throws Exception { final ClassicHttpRequest originalRequest = new BasicClassicHttpRequest("GET", "/"); originalRequest.setVersion(HttpVersion.HTTP_1_0); final ClassicHttpRequest req = originalRequest; req.setHeader("Cache-Control", "no-cache"); final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp); execute(req); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final HttpRequest captured = reqCapture.getValue(); final String via = captured.getFirstHeader("Via").getValue(); final String proto = via.split("\\s+")[0]; Assertions.assertTrue("http/1.0".equalsIgnoreCase(proto) || "1.0".equalsIgnoreCase(proto)); } @Test public void testSetsCacheMissContextIfRequestNotServableFromCache() throws Exception { final ClassicHttpRequest req = new HttpGet("http://foo.example.com/"); req.setHeader("Cache-Control", "no-cache"); final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp); execute(req); Assertions.assertEquals(CacheResponseStatus.CACHE_MISS, context.getCacheResponseStatus()); } @Test public void testSetsViaHeaderOnResponseIfRequestNotServableFromCache() throws Exception { final ClassicHttpRequest req = new HttpGet("http://foo.example.com/"); req.setHeader("Cache-Control", "no-cache"); final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp); final ClassicHttpResponse result = execute(req); Assertions.assertNotNull(result.getFirstHeader("Via")); } @Test public void testSetsViaHeaderOnResponseForCacheMiss() throws Exception { final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result = execute(req1); Assertions.assertNotNull(result.getFirstHeader("Via")); } @Test public void testSetsCacheHitContextIfRequestServedFromCache() throws Exception { final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); execute(req2); Assertions.assertEquals(CacheResponseStatus.CACHE_HIT, context.getCacheResponseStatus()); } @Test public void testSetsViaHeaderOnResponseIfRequestServedFromCache() throws Exception { final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertNotNull(result.getFirstHeader("Via")); } @Test public void testReturns304ForIfModifiedSinceHeaderIfRequestServedFromCache() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(now)); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); } @Test public void testReturns304ForIfModifiedSinceHeaderIf304ResponseInCache() throws Exception { final Instant now = Instant.now(); final Instant oneHourAgo = now.minus(1, ChronoUnit.HOURS); final Instant inTenMinutes = now.plus(10, ChronoUnit.MINUTES); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); req1.addHeader("If-Modified-Since", DateUtils.formatStandardDate(oneHourAgo)); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(oneHourAgo)); final ClassicHttpResponse resp1 = HttpTestUtils.make304Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-control", "max-age=600"); resp1.setHeader("Expires", DateUtils.formatStandardDate(inTenMinutes)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); Assertions.assertFalse(result.containsHeader("Last-Modified")); Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any()); } @Test public void testReturns200ForIfModifiedSinceDateIsLess() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "public, max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now())); // The variant has been modified since this date req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_OK, result.getCode()); } @Test public void testReturns200ForIfModifiedSinceDateIsInvalid() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAfter = now.plusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "public, max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now())); // invalid date (date in the future) req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(tenSecondsAfter)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_OK, result.getCode()); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } @Test public void testReturns304ForIfNoneMatchHeaderIfRequestServedFromCache() throws Exception { final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); req2.addHeader("If-None-Match", "*"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); } @Test public void testReturns200ForIfNoneMatchHeaderFails() throws Exception { final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp1.setHeader("Cache-Control", "public, max-age=3600"); req2.addHeader("If-None-Match", "\"abc\""); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(200, result.getCode()); } @Test public void testReturns304ForIfNoneMatchHeaderAndIfModifiedSinceIfRequestServedFromCache() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now())); req2.addHeader("If-None-Match", "*"); req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); } @Test public void testReturns200ForIfNoneMatchHeaderFailsIfModifiedSinceIgnored() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); req2.addHeader("If-None-Match", "\"abc\""); req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(now)); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(200, result.getCode()); } @Test public void testReturns200ForOptionsFollowedByGetIfAuthorizationHeaderAndSharedCache() throws Exception { impl = new CachingExec(cache, null, CacheConfig.custom().setSharedCache(true).build()); final Instant now = Instant.now(); final ClassicHttpRequest req1 = new HttpOptions("http://foo.example.com/"); req1.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); req2.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); resp1.setHeader("Content-Length", "0"); resp1.setHeader("ETag", "\"options-etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "public, max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(now)); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"get-etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "public, max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(200, result.getCode()); } @Test public void testSetsValidatedContextIfRequestWasSuccessfullyValidated() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setEntity(HttpTestUtils.makeBody(128)); resp2.setHeader("Content-Length", "128"); resp2.setHeader("ETag", "\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp2.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); Assertions.assertEquals(CacheResponseStatus.VALIDATED, context.getCacheResponseStatus()); } @Test public void testSetsViaHeaderIfRequestWasSuccessfullyValidated() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setEntity(HttpTestUtils.makeBody(128)); resp2.setHeader("Content-Length", "128"); resp2.setHeader("ETag", "\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp2.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertNotNull(result.getFirstHeader("Via")); } @Test public void testSetsModuleResponseContextIfValidationRequiredButFailed() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5, must-revalidate"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException()); execute(req2); Assertions.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus()); } @Test public void testSetsModuleResponseContextIfValidationFailsButNotRequired() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException()); execute(req2); Assertions.assertEquals(CacheResponseStatus.CACHE_HIT, context.getCacheResponseStatus()); } @Test public void testSetViaHeaderIfValidationFailsButNotRequired() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException()); final ClassicHttpResponse result = execute(req2); Assertions.assertNotNull(result.getFirstHeader("Via")); } @Test public void testReturns304ForIfNoneMatchPassesIfRequestServedFromOrigin() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); req2.addHeader("If-None-Match", "\"etag\""); final ClassicHttpResponse resp2 = HttpTestUtils.make304Response(); resp2.setHeader("ETag", "\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); } @Test public void testReturns200ForIfNoneMatchFailsIfRequestServedFromOrigin() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); req2.addHeader("If-None-Match", "\"etag\""); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setEntity(HttpTestUtils.makeBody(128)); resp2.setHeader("Content-Length", "128"); resp2.setHeader("ETag", "\"newetag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp2.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_OK, result.getCode()); } @Test public void testReturns304ForIfModifiedSincePassesIfRequestServedFromOrigin() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpResponse resp2 = HttpTestUtils.make304Response(); resp2.setHeader("ETag", "\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); resp2.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); } @Test public void testReturns200ForIfModifiedSinceFailsIfRequestServedFromOrigin() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); req2.addHeader("If-Modified-Since", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setEntity(HttpTestUtils.makeBody(128)); resp2.setHeader("Content-Length", "128"); resp2.setHeader("ETag", "\"newetag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(now)); resp2.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_OK, result.getCode()); } @Test public void testVariantMissServerIfReturns304CacheReturns200() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com"); req1.addHeader("Accept-Encoding", "gzip"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("Etag", "\"gzip_etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Vary", "Accept-Encoding"); resp1.setHeader("Cache-Control", "public, max-age=3600"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com"); req2.addHeader("Accept-Encoding", "deflate"); final ClassicHttpRequest req2Server = new HttpGet("http://foo.example.com"); req2Server.addHeader("Accept-Encoding", "deflate"); req2Server.addHeader("If-None-Match", "\"gzip_etag\""); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setEntity(HttpTestUtils.makeBody(128)); resp2.setHeader("Content-Length", "128"); resp2.setHeader("Etag", "\"deflate_etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Vary", "Accept-Encoding"); resp2.setHeader("Cache-Control", "public, max-age=3600"); final ClassicHttpRequest req3 = new HttpGet("http://foo.example.com"); req3.addHeader("Accept-Encoding", "gzip,deflate"); final ClassicHttpRequest req3Server = new HttpGet("http://foo.example.com"); req3Server.addHeader("Accept-Encoding", "gzip,deflate"); req3Server.addHeader("If-None-Match", "\"gzip_etag\",\"deflate_etag\""); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Content-Length", "128"); resp3.setHeader("Etag", "\"gzip_etag\""); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); resp3.setHeader("Vary", "Accept-Encoding"); resp3.setHeader("Cache-Control", "public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result1 = execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result2 = execute(req2); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); final ClassicHttpResponse result3 = execute(req3); Assertions.assertEquals(HttpStatus.SC_OK, result1.getCode()); Assertions.assertEquals(HttpStatus.SC_OK, result2.getCode()); Assertions.assertEquals(HttpStatus.SC_OK, result3.getCode()); } @Test public void testVariantsMissServerReturns304CacheReturns304() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com"); req1.addHeader("Accept-Encoding", "gzip"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("Etag", "\"gzip_etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Vary", "Accept-Encoding"); resp1.setHeader("Cache-Control", "public, max-age=3600"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com"); req2.addHeader("Accept-Encoding", "deflate"); final ClassicHttpRequest req2Server = new HttpGet("http://foo.example.com"); req2Server.addHeader("Accept-Encoding", "deflate"); req2Server.addHeader("If-None-Match", "\"gzip_etag\""); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setEntity(HttpTestUtils.makeBody(128)); resp2.setHeader("Content-Length", "128"); resp2.setHeader("Etag", "\"deflate_etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Vary", "Accept-Encoding"); resp2.setHeader("Cache-Control", "public, max-age=3600"); final ClassicHttpRequest req4 = new HttpGet("http://foo.example.com"); req4.addHeader("Accept-Encoding", "gzip,identity"); req4.addHeader("If-None-Match", "\"gzip_etag\""); final ClassicHttpRequest req4Server = new HttpGet("http://foo.example.com"); req4Server.addHeader("Accept-Encoding", "gzip,identity"); req4Server.addHeader("If-None-Match", "\"gzip_etag\""); final ClassicHttpResponse resp4 = HttpTestUtils.make304Response(); resp4.setHeader("Etag", "\"gzip_etag\""); resp4.setHeader("Date", DateUtils.formatStandardDate(now)); resp4.setHeader("Vary", "Accept-Encoding"); resp4.setHeader("Cache-Control", "public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result1 = execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result2 = execute(req2); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp4); final ClassicHttpResponse result4 = execute(req4); Assertions.assertEquals(HttpStatus.SC_OK, result1.getCode()); Assertions.assertEquals(HttpStatus.SC_OK, result2.getCode()); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result4.getCode()); } @Test public void testSocketTimeoutExceptionIsNotSilentlyCatched() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp1.setEntity(new InputStreamEntity(new InputStream() { private boolean closed; @Override public void close() throws IOException { closed = true; } @Override public int read() throws IOException { if (closed) { throw new SocketException("Socket closed"); } throw new SocketTimeoutException("Read timed out"); } }, 128, null)); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); Assertions.assertThrows(SocketTimeoutException.class, () -> { final ClassicHttpResponse result1 = execute(req1); EntityUtils.toString(result1.getEntity()); }); } @Test public void testIsSharedCache() { Assertions.assertTrue(config.isSharedCache()); } @Test public void testTooLargeResponsesAreNotCached() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar"); final Instant now = Instant.now(); final Instant requestSent = now.plusSeconds(3); final Instant responseGenerated = now.plusSeconds(2); final Instant responseReceived = now.plusSeconds(1); final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES + 1)); originResponse.setHeader("Cache-Control","public, max-age=3600"); originResponse.setHeader("Date", DateUtils.formatStandardDate(responseGenerated)); originResponse.setHeader("ETag", "\"etag\""); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context); impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived); Mockito.verify(cache, Mockito.never()).createCacheEntry( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testSmallEnoughResponsesAreCached() throws Exception { final HttpCache mockCache = Mockito.mock(HttpCache.class); impl = new CachingExec(mockCache, null, CacheConfig.DEFAULT); final HttpHost host = new HttpHost("foo.example.com"); final ClassicHttpRequest request = new HttpGet("http://foo.example.com/bar"); final Instant now = Instant.now(); final Instant requestSent = now.plusSeconds(3); final Instant responseGenerated = now.plusSeconds(2); final Instant responseReceived = now.plusSeconds(1); final ClassicHttpResponse originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES - 1)); originResponse.setHeader("Cache-Control","public, max-age=3600"); originResponse.setHeader("Date", DateUtils.formatStandardDate(responseGenerated)); originResponse.setHeader("ETag", "\"etag\""); final HttpCacheEntry httpCacheEntry = HttpTestUtils.makeCacheEntry(); final SimpleHttpResponse response = SimpleHttpResponse.create(HttpStatus.SC_OK); Mockito.when(mockCache.createCacheEntry( Mockito.eq(host), RequestEquivalent.eq(request), ResponseEquivalent.eq(response), Mockito.any(), Mockito.eq(requestSent), Mockito.eq(responseReceived))).thenReturn(httpCacheEntry); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context); impl.cacheAndReturnResponse(host, request, originResponse, scope, requestSent, responseReceived); Mockito.verify(mockCache).createCacheEntry( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testIfOnlyIfCachedAndNoCacheEntryBackendNotCalled() throws Exception { request.addHeader("Cache-Control", "only-if-cached"); final ClassicHttpResponse resp = execute(request); Assertions.assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, resp.getCode()); } @Test public void testSetsRouteInContextOnCacheHit() throws Exception { final ClassicHttpResponse response = HttpTestUtils.make200Response(); response.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response); final HttpClientContext ctx = HttpClientContext.create(); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain); Assertions.assertEquals(route, ctx.getHttpRoute()); } @Test public void testSetsRequestInContextOnCacheHit() throws Exception { final ClassicHttpResponse response = HttpTestUtils.make200Response(); response.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response); final HttpClientContext ctx = HttpClientContext.create(); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain); if (!HttpTestUtils.equivalent(request, ctx.getRequest())) { assertSame(request, ctx.getRequest()); } } @Test public void testSetsResponseInContextOnCacheHit() throws Exception { final ClassicHttpResponse response = HttpTestUtils.make200Response(); response.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response); final HttpClientContext ctx = HttpClientContext.create(); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); final ClassicHttpResponse result = impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), null); if (!HttpTestUtils.equivalent(result, ctx.getResponse())) { assertSame(result, ctx.getResponse()); } } @Test public void testSetsRequestSentInContextOnCacheHit() throws Exception { final ClassicHttpResponse response = HttpTestUtils.make200Response(); response.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response); final HttpClientContext ctx = HttpClientContext.create(); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain); } @Test public void testCanCacheAResponseWithoutABody() throws Exception { final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); response.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); response.setHeader("Cache-Control", "max-age=300"); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any()); } @Test public void testNoEntityForIfNoneMatchRequestNotYetInCache() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); req1.addHeader("If-None-Match", "\"etag\""); final ClassicHttpResponse resp1 = HttpTestUtils.make304Response(); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result = execute(req1); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); Assertions.assertNull(result.getEntity(), "The 304 response messages MUST NOT contain a message-body"); } @Test public void testNotModifiedResponseUpdatesCacheEntryWhenNoEntity() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); req1.addHeader("If-None-Match", "etag"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); req2.addHeader("If-None-Match", "etag"); final ClassicHttpResponse resp1 = HttpTestUtils.make304Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "max-age=0"); resp1.setHeader("Etag", "etag"); final ClassicHttpResponse resp2 = HttpTestUtils.make304Response(); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Cache-Control", "max-age=0"); resp1.setHeader("Etag", "etag"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result1 = execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result2 = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode()); Assertions.assertEquals("etag", result1.getFirstHeader("Etag").getValue()); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result2.getCode()); Assertions.assertEquals("etag", result2.getFirstHeader("Etag").getValue()); } @Test public void testNotModifiedResponseWithVaryUpdatesCacheEntryWhenNoEntity() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); req1.addHeader("If-None-Match", "etag"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); req2.addHeader("If-None-Match", "etag"); final ClassicHttpResponse resp1 = HttpTestUtils.make304Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "max-age=0"); resp1.setHeader("Etag", "etag"); resp1.setHeader("Vary", "Accept-Encoding"); final ClassicHttpResponse resp2 = HttpTestUtils.make304Response(); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Cache-Control", "max-age=0"); resp1.setHeader("Etag", "etag"); resp1.setHeader("Vary", "Accept-Encoding"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result1 = execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result2 = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode()); Assertions.assertEquals("etag", result1.getFirstHeader("Etag").getValue()); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result2.getCode()); Assertions.assertEquals("etag", result2.getFirstHeader("Etag").getValue()); } @Test public void testDoesNotSend304ForNonConditionalRequest() throws Exception { final Instant now = Instant.now(); final Instant inOneMinute = now.plus(1, ChronoUnit.MINUTES); final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); req1.addHeader("If-None-Match", "etag"); final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = HttpTestUtils.make304Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "public, max-age=60"); resp1.setHeader("Expires", DateUtils.formatStandardDate(inOneMinute)); resp1.setHeader("Etag", "etag"); resp1.setHeader("Vary", "Accept-Encoding"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "Ok"); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Cache-Control", "public, max-age=60"); resp2.setHeader("Expires", DateUtils.formatStandardDate(inOneMinute)); resp2.setHeader("Etag", "etag"); resp2.setHeader("Vary", "Accept-Encoding"); resp2.setEntity(HttpTestUtils.makeBody(128)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse result1 = execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result2 = execute(req2); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, result1.getCode()); Assertions.assertNull(result1.getEntity()); Assertions.assertEquals(HttpStatus.SC_OK, result2.getCode()); Assertions.assertNotNull(result2.getEntity()); } @Test public void testUsesVirtualHostForCacheKey() throws Exception { final ClassicHttpResponse response = HttpTestUtils.make200Response(); response.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(response); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); request.setAuthority(new URIAuthority("bar.example.com")); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } } TestCombinedEntity.java000066400000000000000000000047661434266521000403320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.util.ByteArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestCombinedEntity { @Test public void testCombinedEntityBasics() throws Exception { final HttpEntity httpEntity = mock(HttpEntity.class); when(httpEntity.getContent()).thenReturn( new ByteArrayInputStream(new byte[] { 6, 7, 8, 9, 10 })); final ByteArrayBuffer buf = new ByteArrayBuffer(1024); final byte[] tmp = new byte[] { 1, 2, 3, 4, 5 }; buf.append(tmp, 0, tmp.length); final CombinedEntity entity = new CombinedEntity(httpEntity, buf); Assertions.assertEquals(-1, entity.getContentLength()); Assertions.assertFalse(entity.isRepeatable()); Assertions.assertTrue(entity.isStreaming()); Assertions.assertArrayEquals(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, EntityUtils.toByteArray(entity)); verify(httpEntity).getContent(); entity.close(); verify(httpEntity).close(); } } TestConditionalRequestBuilder.java000066400000000000000000000332261434266521000425310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.hc.client5.http.cache.HeaderConstants; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestConditionalRequestBuilder { private ConditionalRequestBuilder impl; private HttpRequest request; @BeforeEach public void setUp() throws Exception { impl = new ConditionalRequestBuilder<>(request -> BasicRequestBuilder.copy(request).build()); request = new BasicHttpRequest("GET", "/"); } @Test public void testBuildConditionalRequestWithLastModified() { final String theMethod = "GET"; final String theUri = "/theuri"; final String lastModified = "this is my last modified date"; final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri); basicRequest.addHeader("Accept-Encoding", "gzip"); final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("Last-Modified", lastModified) }; final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers); final HttpRequest newRequest = impl.buildConditionalRequest(basicRequest, cacheEntry); Assertions.assertEquals(theMethod, newRequest.getMethod()); Assertions.assertEquals(theUri, newRequest.getRequestUri()); Assertions.assertEquals(2, newRequest.getHeaders().length); Assertions.assertEquals("Accept-Encoding", newRequest.getHeaders()[0].getName()); Assertions.assertEquals("gzip", newRequest.getHeaders()[0].getValue()); Assertions.assertEquals("If-Modified-Since", newRequest.getHeaders()[1].getName()); Assertions.assertEquals(lastModified, newRequest.getHeaders()[1].getValue()); } @Test public void testConditionalRequestForEntryWithLastModifiedAndEtagIncludesBothAsValidators() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant twentySecondsAgo = now.plusSeconds(20); final String lmDate = DateUtils.formatStandardDate(twentySecondsAgo); final String etag = "\"etag\""; final Header[] headers = { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("Last-Modified", lmDate), new BasicHeader("ETag", etag) }; final HttpRequest basicRequest = new BasicHttpRequest("GET", "/"); final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers); final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry); Assertions.assertEquals(lmDate, result.getFirstHeader("If-Modified-Since").getValue()); Assertions.assertEquals(etag, result.getFirstHeader("If-None-Match").getValue()); } @Test public void testBuildConditionalRequestWithETag() { final String theMethod = "GET"; final String theUri = "/theuri"; final String theETag = "this is my eTag"; final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri); basicRequest.addHeader("Accept-Encoding", "gzip"); final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now())), new BasicHeader("ETag", theETag) }; final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers); final HttpRequest newRequest = impl.buildConditionalRequest(basicRequest, cacheEntry); Assertions.assertEquals(theMethod, newRequest.getMethod()); Assertions.assertEquals(theUri, newRequest.getRequestUri()); Assertions.assertEquals(3, newRequest.getHeaders().length); Assertions.assertEquals("Accept-Encoding", newRequest.getHeaders()[0].getName()); Assertions.assertEquals("gzip", newRequest.getHeaders()[0].getValue()); Assertions.assertEquals("If-None-Match", newRequest.getHeaders()[1].getName()); Assertions.assertEquals(theETag, newRequest.getHeaders()[1].getValue()); } @Test public void testCacheEntryWithMustRevalidateDoesEndToEndRevalidation() throws Exception { final HttpRequest basicRequest = new BasicHttpRequest("GET","/"); final Instant now = Instant.now(); final Instant elevenSecondsAgo = now.minusSeconds(11); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant nineSecondsAgo = now.plusSeconds(9); final Header[] cacheEntryHeaders = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Cache-Control","max-age=5, must-revalidate") }; final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders); final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry); boolean foundMaxAge0 = false; final Iterator it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) { foundMaxAge0 = true; } } Assertions.assertTrue(foundMaxAge0); } @Test public void testCacheEntryWithProxyRevalidateDoesEndToEndRevalidation() throws Exception { final HttpRequest basicRequest = new BasicHttpRequest("GET", "/"); final Instant now = Instant.now(); final Instant elevenSecondsAgo = now.minusSeconds(11); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant nineSecondsAgo = now.plusSeconds(9); final Header[] cacheEntryHeaders = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Cache-Control","max-age=5, proxy-revalidate") }; final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders); final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry); boolean foundMaxAge0 = false; final Iterator it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) { foundMaxAge0 = true; } } Assertions.assertTrue(foundMaxAge0); } @Test public void testBuildUnconditionalRequestUsesGETMethod() throws Exception { final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertEquals("GET", result.getMethod()); } @Test public void testBuildUnconditionalRequestUsesRequestUri() throws Exception { final String uri = "/theURI"; request = new BasicHttpRequest("GET", uri); final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertEquals(uri, result.getRequestUri()); } @Test public void testBuildUnconditionalRequestAddsCacheControlNoCache() throws Exception { final HttpRequest result = impl.buildUnconditionalRequest(request); boolean ccNoCacheFound = false; final Iterator it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("no-cache".equals(elt.getName())) { ccNoCacheFound = true; } } Assertions.assertTrue(ccNoCacheFound); } @Test public void testBuildUnconditionalRequestAddsPragmaNoCache() throws Exception { final HttpRequest result = impl.buildUnconditionalRequest(request); boolean ccNoCacheFound = false; final Iterator it = MessageSupport.iterate(result, HeaderConstants.PRAGMA); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("no-cache".equals(elt.getName())) { ccNoCacheFound = true; } } Assertions.assertTrue(ccNoCacheFound); } @Test public void testBuildUnconditionalRequestDoesNotUseIfRange() throws Exception { request.addHeader("If-Range","\"etag\""); final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertNull(result.getFirstHeader("If-Range")); } @Test public void testBuildUnconditionalRequestDoesNotUseIfMatch() throws Exception { request.addHeader("If-Match","\"etag\""); final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertNull(result.getFirstHeader("If-Match")); } @Test public void testBuildUnconditionalRequestDoesNotUseIfNoneMatch() throws Exception { request.addHeader("If-None-Match","\"etag\""); final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertNull(result.getFirstHeader("If-None-Match")); } @Test public void testBuildUnconditionalRequestDoesNotUseIfUnmodifiedSince() throws Exception { request.addHeader("If-Unmodified-Since", DateUtils.formatStandardDate(Instant.now())); final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertNull(result.getFirstHeader("If-Unmodified-Since")); } @Test public void testBuildUnconditionalRequestDoesNotUseIfModifiedSince() throws Exception { request.addHeader("If-Modified-Since", DateUtils.formatStandardDate(Instant.now())); final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertNull(result.getFirstHeader("If-Modified-Since")); } @Test public void testBuildUnconditionalRequestCarriesOtherRequestHeaders() throws Exception { request.addHeader("User-Agent","MyBrowser/1.0"); final HttpRequest result = impl.buildUnconditionalRequest(request); Assertions.assertEquals("MyBrowser/1.0", result.getFirstHeader("User-Agent").getValue()); } @Test public void testBuildConditionalRequestFromVariants() throws Exception { final String etag1 = "\"123\""; final String etag2 = "\"456\""; final String etag3 = "\"789\""; final Map variantEntries = new HashMap<>(); variantEntries.put(etag1, new Variant("A", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag1) }))); variantEntries.put(etag2, new Variant("B", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag2) }))); variantEntries.put(etag3, new Variant("C", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag3) }))); final HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries); // seems like a lot of work, but necessary, check for existence and exclusiveness String ifNoneMatch = conditional.getFirstHeader(HeaderConstants.IF_NONE_MATCH).getValue(); Assertions.assertTrue(ifNoneMatch.contains(etag1)); Assertions.assertTrue(ifNoneMatch.contains(etag2)); Assertions.assertTrue(ifNoneMatch.contains(etag3)); ifNoneMatch = ifNoneMatch.replace(etag1, ""); ifNoneMatch = ifNoneMatch.replace(etag2, ""); ifNoneMatch = ifNoneMatch.replace(etag3, ""); ifNoneMatch = ifNoneMatch.replace(",",""); ifNoneMatch = ifNoneMatch.replace(" ", ""); Assertions.assertEquals(ifNoneMatch, ""); } } TestDateSupport.java000066400000000000000000000123431434266521000376550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.hamcrest.MatcherAssert.assertThat; import java.time.Instant; import java.time.LocalDate; import java.time.Month; import java.time.ZoneId; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.HeaderGroup; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; /** * Unit tests for {@link DateSupport}. */ public class TestDateSupport { private static Instant createInstant(final int year, final Month month, final int day) { return LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant(); } @Test public void testIsBefore() throws Exception { final HeaderGroup message1 = new HeaderGroup(); final HeaderGroup message2 = new HeaderGroup(); assertThat(DateSupport.isBefore(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false)); assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?")); message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?")); assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?")); message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26)))); assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25)))); message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26)))); assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27)))); message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26)))); assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); } @Test public void testIsAfter() throws Exception { final HeaderGroup message1 = new HeaderGroup(); final HeaderGroup message2 = new HeaderGroup(); assertThat(DateSupport.isAfter(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false)); assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?")); message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?")); assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?")); message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26)))); assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27)))); message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26)))); assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true)); message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25)))); message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26)))); assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false)); } } TestDefaultAsyncCacheInvalidator.java000066400000000000000000000751661434266521000431220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.net.URI; import java.time.Instant; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; public class TestDefaultAsyncCacheInvalidator { private DefaultAsyncCacheInvalidator impl; private HttpHost host; @Mock private HttpCacheEntry mockEntry; @Mock private Resolver cacheKeyResolver; @Mock private HttpAsyncCacheStorage mockStorage; @Mock private FutureCallback operationCallback; @Mock private Cancellable cancellable; private Instant now; private Instant tenSecondsAgo; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); now = Instant.now(); tenSecondsAgo = now.minusSeconds(10); when(cacheKeyResolver.resolve(ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final URI uri = invocation.getArgument(0); return HttpCacheSupport.normalize(uri).toASCIIString(); }); host = new HttpHost("foo.example.com"); impl = new DefaultAsyncCacheInvalidator(); } // Tests @Test public void testInvalidatesRequestsThatArentGETorHEAD() throws Exception { final HttpRequest request = new BasicHttpRequest("POST","/path"); final String key = "http://foo.example.com:80/path"; final Map variantMap = new HashMap<>(); cacheEntryHasVariantMap(variantMap); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); } @Test public void testInvalidatesUrisInContentLocationHeadersOnPUTs() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String contentLocation = "http://foo.example.com/content"; request.setHeader("Content-Location", contentLocation); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq("http://foo.example.com:80/content"), ArgumentMatchers.any()); } @Test public void testInvalidatesUrisInLocationHeadersOnPUTs() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String contentLocation = "http://foo.example.com/content"; request.setHeader("Location",contentLocation); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq("http://foo.example.com:80/content"), ArgumentMatchers.any()); } @Test public void testInvalidatesRelativeUrisInContentLocationHeadersOnPUTs() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String relativePath = "/content"; request.setHeader("Content-Location",relativePath); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq("http://foo.example.com:80/content"), ArgumentMatchers.any()); } @Test public void testDoesNotInvalidateUrisInContentLocationHeadersOnPUTsToDifferentHosts() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String contentLocation = "http://bar.example.com/content"; request.setHeader("Content-Location",contentLocation); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); } @Test public void testDoesNotInvalidateGETRequest() throws Exception { final HttpRequest request = new BasicHttpRequest("GET","/"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq("http://foo.example.com:80/"), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateHEADRequest() throws Exception { final HttpRequest request = new BasicHttpRequest("HEAD","/"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq("http://foo.example.com:80/"), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void testInvalidatesHEADCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("GET", uri); cacheEntryisForMethod("HEAD"); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getRequestMethod(); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); } @Test public void testInvalidatesVariantHEADCacheEntriesIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("GET", uri); final String theVariantKey = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}"; final String theVariantURI = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}http://foo.example.com:80/"; final Map variants = HttpTestUtils.makeDefaultVariantMap(theVariantKey, theVariantURI); cacheEntryisForMethod("HEAD"); cacheEntryHasVariantMap(variants); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getRequestMethod(); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(theVariantURI), ArgumentMatchers.any()); } @Test public void testDoesNotInvalidateHEADCacheEntry() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("HEAD", uri); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateHEADCacheEntryIfSubsequentHEADRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("HEAD", uri); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateGETCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("GET", uri); cacheEntryisForMethod("GET"); cacheReturnsEntryForUri(key, mockEntry); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockEntry).getRequestMethod(); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateRequestsWithClientCacheControlHeaders() throws Exception { final HttpRequest request = new BasicHttpRequest("GET","/"); request.setHeader("Cache-Control","no-cache"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq("http://foo.example.com:80/"), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateRequestsWithClientPragmaHeaders() throws Exception { final HttpRequest request = new BasicHttpRequest("GET","/"); request.setHeader("Pragma","no-cache"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq("http://foo.example.com:80/"), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void testVariantURIsAreFlushedAlso() throws Exception { final HttpRequest request = new BasicHttpRequest("POST","/"); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final String variantUri = "theVariantURI"; final Map mapOfURIs = HttpTestUtils.makeDefaultVariantMap(variantUri, variantUri); cacheReturnsEntryForUri(key, mockEntry); cacheEntryHasVariantMap(mapOfURIs); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockEntry).getVariantMap(); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(variantUri), ArgumentMatchers.any()); } @Test public void doesNotFlushForResponsesWithoutContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("POST","/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntryIfFresherAndSpecifiedByContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); } @Test public void flushesEntryIfFresherAndSpecifiedByLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(201); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); } @Test public void doesNotFlushEntryForUnsuccessfulResponse() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request"); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntryIfFresherAndSpecifiedByNonCanonicalContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", "http://foo.example.com/bar"); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); } @Test public void flushesEntryIfFresherAndSpecifiedByRelativeContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", "/bar"); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); } @Test public void doesNotFlushEntryIfContentLocationFromDifferentHost() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://baz.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfEtagsMatch() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"same-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"same-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfOlder() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(now)), new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntryIfNotInCache() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); cacheReturnsEntryForUri(key, null); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasNoEtag() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.removeHeaders("ETag"); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasNoEtag() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag", "\"some-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfResponseHasNoDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag", "\"new-etag\""); response.removeHeaders("Date"); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\""), new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfEntryHasNoDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\"") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfResponseHasMalformedDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", "blarg"); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\""), new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)) }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfEntryHasMalformedDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\""), new BasicHeader("Date", "foo") }); cacheReturnsEntryForUri(key, entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage, operationCallback); verify(mockStorage).getEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verify(mockStorage).removeEntry(ArgumentMatchers.eq(key), ArgumentMatchers.any()); verifyNoMoreInteractions(mockStorage); } // Expectations private void cacheEntryHasVariantMap(final Map variantMap) { when(mockEntry.getVariantMap()).thenReturn(variantMap); } private void cacheReturnsEntryForUri(final String key, final HttpCacheEntry cacheEntry) { Mockito.when(mockStorage.getEntry( ArgumentMatchers.eq(key), ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final FutureCallback callback = invocation.getArgument(1); callback.completed(cacheEntry); return cancellable; }); } private void cacheEntryisForMethod(final String httpMethod) { when(mockEntry.getRequestMethod()).thenReturn(httpMethod); } } TestDefaultCacheInvalidator.java000066400000000000000000000662541434266521000421220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.io.IOException; import java.net.URI; import java.time.Instant; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; public class TestDefaultCacheInvalidator { private DefaultCacheInvalidator impl; private HttpHost host; @Mock private HttpCacheEntry mockEntry; @Mock private Resolver cacheKeyResolver; @Mock private HttpCacheStorage mockStorage; private Instant now; private Instant tenSecondsAgo; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); now = Instant.now(); tenSecondsAgo = now.minusSeconds(10); when(cacheKeyResolver.resolve(ArgumentMatchers.any())).thenAnswer((Answer) invocation -> { final URI uri = invocation.getArgument(0); return HttpCacheSupport.normalize(uri).toASCIIString(); }); host = new HttpHost("foo.example.com"); impl = new DefaultCacheInvalidator(); } // Tests @Test public void testInvalidatesRequestsThatArentGETorHEAD() throws Exception { final HttpRequest request = new BasicHttpRequest("POST","/path"); final String key = "http://foo.example.com:80/path"; final Map variantMap = new HashMap<>(); cacheEntryHasVariantMap(variantMap); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); } @Test public void testInvalidatesUrisInContentLocationHeadersOnPUTs() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String contentLocation = "http://foo.example.com/content"; request.setHeader("Content-Location", contentLocation); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verify(mockStorage).removeEntry("http://foo.example.com:80/content"); } @Test public void testInvalidatesUrisInLocationHeadersOnPUTs() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String contentLocation = "http://foo.example.com/content"; request.setHeader("Location",contentLocation); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verify(mockStorage).removeEntry("http://foo.example.com:80/content"); } @Test public void testInvalidatesRelativeUrisInContentLocationHeadersOnPUTs() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String relativePath = "/content"; request.setHeader("Content-Location",relativePath); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verify(mockStorage).removeEntry("http://foo.example.com:80/content"); } @Test public void testDoesNotInvalidateUrisInContentLocationHeadersOnPUTsToDifferentHosts() throws Exception { final HttpRequest request = new BasicHttpRequest("PUT","/"); request.setHeader("Content-Length","128"); final String contentLocation = "http://bar.example.com/content"; request.setHeader("Content-Location",contentLocation); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); } @Test public void testDoesNotInvalidateGETRequest() throws Exception { final HttpRequest request = new BasicHttpRequest("GET","/"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry("http://foo.example.com:80/"); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateHEADRequest() throws Exception { final HttpRequest request = new BasicHttpRequest("HEAD","/"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry("http://foo.example.com:80/"); verifyNoMoreInteractions(mockStorage); } @Test public void testInvalidatesHEADCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("GET", uri); cacheEntryisForMethod("HEAD"); cacheEntryHasVariantMap(new HashMap<>()); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getRequestMethod(); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); } @Test public void testInvalidatesVariantHEADCacheEntriesIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("GET", uri); final String theVariantKey = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}"; final String theVariantURI = "{Accept-Encoding=gzip%2Cdeflate&User-Agent=Apache-HttpClient}http://foo.example.com:80/"; final Map variants = HttpTestUtils.makeDefaultVariantMap(theVariantKey, theVariantURI); cacheEntryisForMethod("HEAD"); cacheEntryHasVariantMap(variants); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getRequestMethod(); verify(mockEntry).getVariantMap(); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verify(mockStorage).removeEntry(theVariantURI); } @Test public void testDoesNotInvalidateHEADCacheEntry() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("HEAD", uri); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateHEADCacheEntryIfSubsequentHEADRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("HEAD", uri); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateGETCacheEntryIfSubsequentGETRequestsAreMadeToTheSameURI() throws Exception { final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final HttpRequest request = new BasicHttpRequest("GET", uri); cacheEntryisForMethod("GET"); cacheReturnsEntryForUri(key); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockEntry).getRequestMethod(); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateRequestsWithClientCacheControlHeaders() throws Exception { final HttpRequest request = new BasicHttpRequest("GET","/"); request.setHeader("Cache-Control","no-cache"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry("http://foo.example.com:80/"); verifyNoMoreInteractions(mockStorage); } @Test public void testDoesNotInvalidateRequestsWithClientPragmaHeaders() throws Exception { final HttpRequest request = new BasicHttpRequest("GET","/"); request.setHeader("Pragma","no-cache"); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry("http://foo.example.com:80/"); verifyNoMoreInteractions(mockStorage); } @Test public void testVariantURIsAreFlushedAlso() throws Exception { final HttpRequest request = new BasicHttpRequest("POST","/"); final URI uri = new URI("http://foo.example.com:80/"); final String key = uri.toASCIIString(); final String variantUri = "theVariantURI"; final Map mapOfURIs = HttpTestUtils.makeDefaultVariantMap(variantUri, variantUri); cacheReturnsEntryForUri(key); cacheEntryHasVariantMap(mapOfURIs); impl.flushCacheEntriesInvalidatedByRequest(host, request, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verify(mockEntry).getVariantMap(); verify(mockStorage).removeEntry(variantUri); verify(mockStorage).removeEntry(key); } @Test public void doesNotFlushForResponsesWithoutContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("POST","/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntryIfFresherAndSpecifiedByContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); } @Test public void flushesEntryIfFresherAndSpecifiedByLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(201); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); } @Test public void doesNotFlushEntryForUnsuccessfulResponse() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request"); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntryIfFresherAndSpecifiedByNonCanonicalContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String cacheKey = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", "http://foo.example.com/bar"); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(cacheKey)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(cacheKey); verify(mockStorage).removeEntry(cacheKey); } @Test public void flushesEntryIfFresherAndSpecifiedByRelativeContentLocation() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String cacheKey = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", "/bar"); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(cacheKey)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(cacheKey); verify(mockStorage).removeEntry(cacheKey); } @Test public void doesNotFlushEntryIfContentLocationFromDifferentHost() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String cacheKey = "http://baz.example.com:80/bar"; response.setHeader("Content-Location", cacheKey); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfEtagsMatch() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"same-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"same-etag\"") }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfOlder() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(now)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntryIfNotInCache() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); when(mockStorage.getEntry(key)).thenReturn(null); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasNoEtag() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.removeHeaders("ETag"); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasNoEtag() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag", "\"some-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfResponseHasNoDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag", "\"new-etag\""); response.removeHeaders("Date"); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\""), new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)), }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfEntryHasNoDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfResponseHasMalformedDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", "blarg"); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\""), new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)) }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verifyNoMoreInteractions(mockStorage); } @Test public void flushesEntrySpecifiedByContentLocationIfEntryHasMalformedDate() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatStandardDate(now)); final String key = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", key); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\""), new BasicHeader("Date", "foo") }); when(mockStorage.getEntry(key)).thenReturn(entry); impl.flushCacheEntriesInvalidatedByExchange(host, request, response, cacheKeyResolver, mockStorage); verify(mockStorage).getEntry(key); verify(mockStorage).removeEntry(key); verifyNoMoreInteractions(mockStorage); } // Expectations private void cacheEntryHasVariantMap(final Map variantMap) { when(mockEntry.getVariantMap()).thenReturn(variantMap); } private void cacheReturnsEntryForUri(final String key) throws IOException { when(mockStorage.getEntry(key)).thenReturn(mockEntry); } private void cacheEntryisForMethod(final String httpMethod) { when(mockEntry.getRequestMethod()).thenReturn(httpMethod); } } TestHttpByteArrayCacheEntrySerializer.java000066400000000000000000000432331434266521000441470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.apache.hc.client5.http.impl.cache.HttpByteArrayCacheEntrySerializerTestUtils.HttpCacheStorageEntryTestTemplate; import static org.apache.hc.client5.http.impl.cache.HttpByteArrayCacheEntrySerializerTestUtils.httpCacheStorageEntryFromBytes; import static org.apache.hc.client5.http.impl.cache.HttpByteArrayCacheEntrySerializerTestUtils.makeTestFileObject; import static org.apache.hc.client5.http.impl.cache.HttpByteArrayCacheEntrySerializerTestUtils.readTestFileBytes; import static org.apache.hc.client5.http.impl.cache.HttpByteArrayCacheEntrySerializerTestUtils.testWithCache; import static org.apache.hc.client5.http.impl.cache.HttpByteArrayCacheEntrySerializerTestUtils.verifyHttpCacheEntryFromTestFile; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.cache.HttpCacheEntrySerializer; import org.apache.hc.client5.http.cache.HttpCacheStorageEntry; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.impl.io.AbstractMessageParser; import org.apache.hc.core5.http.impl.io.AbstractMessageWriter; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.io.SessionOutputBuffer; import org.apache.hc.core5.http.message.BasicHeader; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestHttpByteArrayCacheEntrySerializer { private static final String SERIALIAZED_EXTENSION = ".httpbytes.serialized"; private static final String FILE_TEST_SERIALIZED_NAME = "ApacheLogo" + SERIALIAZED_EXTENSION; private static final String SIMPLE_OBJECT_SERIALIZED_NAME = "simpleObject" + SERIALIAZED_EXTENSION; private static final String VARIANTMAP_TEST_SERIALIZED_NAME = "variantMap" + SERIALIAZED_EXTENSION; private static final String ESCAPED_HEADER_TEST_SERIALIZED_NAME = "escapedHeader" + SERIALIAZED_EXTENSION; private static final String NO_BODY_TEST_SERIALIZED_NAME = "noBody" + SERIALIAZED_EXTENSION; private static final String MISSING_HEADER_TEST_SERIALIZED_NAME = "missingHeader" + SERIALIAZED_EXTENSION; private static final String INVALID_HEADER_TEST_SERIALIZED_NAME = "invalidHeader" + SERIALIAZED_EXTENSION; private static final String VARIANTMAP_MISSING_KEY_TEST_SERIALIZED_NAME = "variantMapMissingKey" + SERIALIAZED_EXTENSION; private static final String VARIANTMAP_MISSING_VALUE_TEST_SERIALIZED_NAME = "variantMapMissingValue" + SERIALIAZED_EXTENSION; private static final String TEST_CONTENT_FILE_NAME = "ApacheLogo.png"; private HttpCacheEntrySerializer serializer; // Manually set this to true to re-generate all of the serialized files private final boolean reserializeFiles = false; @BeforeEach public void before() { serializer = HttpByteArrayCacheEntrySerializer.INSTANCE; } /** * Deserialize a cache entry in a bad format, expecting an exception. * * @throws Exception is expected */ @Test public void testInvalidCacheEntry() throws Exception { // This file is a JPEG not a cache entry, so should fail to deserialize final byte[] bytes = readTestFileBytes(TEST_CONTENT_FILE_NAME); Assertions.assertThrows(ResourceIOException.class, () -> httpCacheStorageEntryFromBytes(serializer, bytes)); } /** * Deserialize a cache entry with a missing header, from a previously saved file. * * @throws Exception is expected */ @Test public void testMissingHeaderCacheEntry() throws Exception { // This file hand-edited to be missing a necessary header final byte[] bytes = readTestFileBytes(MISSING_HEADER_TEST_SERIALIZED_NAME); Assertions.assertThrows(ResourceIOException.class, () -> httpCacheStorageEntryFromBytes(serializer, bytes)); } /** * Deserialize a cache entry with an invalid header value, from a previously saved file. * * @throws Exception is expected */ @Test public void testInvalidHeaderCacheEntry() throws Exception { // This file hand-edited to have an invalid header final byte[] bytes = readTestFileBytes(INVALID_HEADER_TEST_SERIALIZED_NAME); Assertions.assertThrows(ResourceIOException.class, () -> httpCacheStorageEntryFromBytes(serializer, bytes)); } /** * Deserialize a cache entry with a missing variant map key, from a previously saved file. * * @throws Exception is expected */ @Test public void testVariantMapMissingKeyCacheEntry() throws Exception { // This file hand-edited to be missing a VariantCache key final byte[] bytes = readTestFileBytes(VARIANTMAP_MISSING_KEY_TEST_SERIALIZED_NAME); Assertions.assertThrows(ResourceIOException.class, () -> httpCacheStorageEntryFromBytes(serializer, bytes)); } /** * Deserialize a cache entry with a missing variant map value, from a previously saved file. * * @throws Exception is expected */ @Test public void testVariantMapMissingValueCacheEntry() throws Exception { // This file hand-edited to be missing a VariantCache value final byte[] bytes = readTestFileBytes(VARIANTMAP_MISSING_VALUE_TEST_SERIALIZED_NAME); Assertions.assertThrows(ResourceIOException.class, () -> httpCacheStorageEntryFromBytes(serializer, bytes)); } /** * Test an IOException being thrown while deserializing. * * @throws Exception is expected */ @Test public void testDeserializeWithIOException() throws Exception { final AbstractMessageParser throwyParser = Mockito.mock(AbstractMessageParser.class); Mockito. doThrow(new IOException("Test Exception")). when(throwyParser). parse(Mockito.any(SessionInputBuffer.class), Mockito.any(InputStream.class)); final HttpByteArrayCacheEntrySerializer testSerializer = new HttpByteArrayCacheEntrySerializer() { @Override protected AbstractMessageParser makeHttpResponseParser() { return throwyParser; } }; Assertions.assertThrows(ResourceIOException.class, () -> testSerializer.deserialize(new byte[0])); } ////////////// Using new Constructor with Instant ////////////// /** * Serialize and deserialize a simple object with a tiny body. * * @throws Exception if anything goes wrong */ @Test public void simpleObjectTest() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); testWithCache(serializer, testEntry); } /** * Serialize and deserialize a larger object with a binary file for a body. * * @throws Exception if anything goes wrong */ @Test public void fileObjectTest() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.resource = new FileResource(makeTestFileObject(TEST_CONTENT_FILE_NAME)); final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); testWithCache(serializer, testEntry); } /** * Serialize and deserialize a cache entry with no headers. * * @throws Exception if anything goes wrong */ @Test public void noHeadersTest() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.responseHeaders = new Header[0]; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); testWithCache(serializer, testEntry); } /** * Serialize and deserialize a cache entry with an empty body. * * @throws Exception if anything goes wrong */ @Test public void emptyBodyTest() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.resource = new HeapResource(new byte[0]); final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); testWithCache(serializer, testEntry); } /** * Serialize and deserialize a cache entry with no body. * * @throws Exception if anything goes wrong */ @Test public void noBodyTest() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.resource = null; cacheObjectValues.responseCode = 204; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); testWithCache(serializer, testEntry); } /** * Serialize and deserialize a cache entry with a variant map. * * @throws Exception if anything goes wrong */ @Test public void testSimpleVariantMap() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); final Map variantMap = new HashMap<>(); variantMap.put("{Accept-Encoding=gzip}","{Accept-Encoding=gzip}https://example.com:1234/foo"); variantMap.put("{Accept-Encoding=compress}","{Accept-Encoding=compress}https://example.com:1234/foo"); cacheObjectValues.variantMap = variantMap; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); testWithCache(serializer, testEntry); } /** * Ensures that if the server uses our reserved header names we don't mix them up with our own pseudo-headers. * * @throws Exception if anything goes wrong */ @Test public void testEscapedHeaders() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.responseHeaders = new Header[] { new BasicHeader("hc-test-1", "hc-test-1-value"), new BasicHeader("hc-sk", "hc-sk-value"), new BasicHeader("hc-resp-date", "hc-resp-date-value"), new BasicHeader("hc-req-date-date", "hc-req-date-value"), new BasicHeader("hc-varmap-key", "hc-varmap-key-value"), new BasicHeader("hc-varmap-val", "hc-varmap-val-value"), }; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); testWithCache(serializer, testEntry); } /** * Attempt to store a cache entry with a null storage key. * */ @Test public void testNullStorageKey() { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.storageKey = null; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); Assertions.assertThrows(IllegalStateException.class, () -> serializer.serialize(testEntry)); } /** * Deserialize a simple object, from a previously saved file. * * Ensures that if the serialization format changes in an incompatible way, we'll find out in a test. * * @throws Exception if anything goes wrong */ @Test public void simpleTestFromPreviouslySerialized() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); verifyHttpCacheEntryFromTestFile(serializer, testEntry, SIMPLE_OBJECT_SERIALIZED_NAME, reserializeFiles); } /** * Deserialize a larger object with a binary body, from a previously saved file. * * Ensures that if the serialization format changes in an incompatible way, we'll find out in a test. * * @throws Exception if anything goes wrong */ @Test public void fileTestFromPreviouslySerialized() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.resource = new FileResource(makeTestFileObject(TEST_CONTENT_FILE_NAME)); final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); verifyHttpCacheEntryFromTestFile(serializer, testEntry, FILE_TEST_SERIALIZED_NAME, reserializeFiles); } /** * Deserialize a cache entry with a variant map, from a previously saved file. * * @throws Exception if anything goes wrong */ @Test public void variantMapTestFromPreviouslySerialized() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); final Map variantMap = new HashMap<>(); variantMap.put("{Accept-Encoding=gzip}","{Accept-Encoding=gzip}https://example.com:1234/foo"); variantMap.put("{Accept-Encoding=compress}","{Accept-Encoding=compress}https://example.com:1234/foo"); cacheObjectValues.variantMap = variantMap; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); verifyHttpCacheEntryFromTestFile(serializer, testEntry, VARIANTMAP_TEST_SERIALIZED_NAME, reserializeFiles); } /** * Deserialize a cache entry with headers that use our pseudo-header prefix and need escaping. * * @throws Exception if anything goes wrong */ @Test public void escapedHeaderTestFromPreviouslySerialized() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.responseHeaders = new Header[] { new BasicHeader("hc-test-1", "hc-test-1-value"), new BasicHeader("hc-sk", "hc-sk-value"), new BasicHeader("hc-resp-date", "hc-resp-date-value"), new BasicHeader("hc-req-date-date", "hc-req-date-value"), new BasicHeader("hc-varmap-key", "hc-varmap-key-value"), new BasicHeader("hc-varmap-val", "hc-varmap-val-value"), }; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); verifyHttpCacheEntryFromTestFile(serializer, testEntry, ESCAPED_HEADER_TEST_SERIALIZED_NAME, reserializeFiles); } /** * Deserialize a cache entry with no body, from a previously saved file. * * @throws Exception if anything goes wrong */ @Test public void noBodyTestFromPreviouslySerialized() throws Exception { final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); cacheObjectValues.resource = null; cacheObjectValues.responseCode = 204; final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); verifyHttpCacheEntryFromTestFile(serializer, testEntry, NO_BODY_TEST_SERIALIZED_NAME, reserializeFiles); } /** * Test an HttpException being thrown while serializing. * * @throws Exception is expected */ @Test public void testSerializeWithHTTPException() throws Exception { final AbstractMessageWriter throwyHttpWriter = Mockito.mock(AbstractMessageWriter.class); Mockito. doThrow(new HttpException("Test Exception")). when(throwyHttpWriter). write(Mockito.any(SimpleHttpResponse.class), Mockito.any(SessionOutputBuffer.class), Mockito.any(OutputStream.class)); final HttpCacheStorageEntryTestTemplate cacheObjectValues = HttpCacheStorageEntryTestTemplate.makeDefault(); final HttpCacheStorageEntry testEntry = cacheObjectValues.toEntry(); final HttpByteArrayCacheEntrySerializer testSerializer = new HttpByteArrayCacheEntrySerializer() { @Override protected AbstractMessageWriter makeHttpResponseWriter(final SessionOutputBuffer outputBuffer) { return throwyHttpWriter; } }; Assertions.assertThrows(ResourceIOException.class, () -> testSerializer.serialize(testEntry)); } } TestHttpCacheJiraNumber1147.java000066400000000000000000000135531434266521000415460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.File; import java.time.Instant; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.cache.CacheResponseStatus; import org.apache.hc.client5.http.cache.HttpCacheContext; import org.apache.hc.client5.http.cache.HttpCacheStorage; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestHttpCacheJiraNumber1147 { private File cacheDir; private void removeCache() { if (this.cacheDir != null) { final File[] files = this.cacheDir.listFiles(); for (final File cacheFile : files) { cacheFile.delete(); } this.cacheDir.delete(); this.cacheDir = null; } } @BeforeEach public void setUp() throws Exception { cacheDir = File.createTempFile("cachedir", ""); if (cacheDir.exists()) { cacheDir.delete(); } cacheDir.mkdir(); } @AfterEach public void cleanUp() { removeCache(); } @Test public void testIssue1147() throws Exception { final CacheConfig cacheConfig = CacheConfig.custom() .setSharedCache(true) .setMaxObjectSize(262144) //256kb .build(); final ResourceFactory resourceFactory = new FileResourceFactory(cacheDir); final HttpCacheStorage httpCacheStorage = new ManagedHttpCacheStorage(cacheConfig); final ExecChain mockExecChain = mock(ExecChain.class); final ExecRuntime mockEndpoint = mock(ExecRuntime.class); final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); final ClassicHttpRequest get = new HttpGet("http://somehost/"); final HttpCacheContext context = HttpCacheContext.create(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.setEntity(HttpTestUtils.makeBody(128)); response.setHeader("Content-Length", "128"); response.setHeader("ETag", "\"etag\""); response.setHeader("Cache-Control", "public, max-age=3600"); response.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); when(mockExecChain.proceed( isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class))).thenReturn(response); final BasicHttpCache cache = new BasicHttpCache(resourceFactory, httpCacheStorage); final ExecChainHandler t = createCachingExecChain(cache, cacheConfig); final ExecChain.Scope scope = new ExecChain.Scope("teset", route, get, mockEndpoint, context); final ClassicHttpResponse response1 = t.execute(get, scope, mockExecChain); Assertions.assertEquals(200, response1.getCode()); EntityUtils.consume(response1.getEntity()); verify(mockExecChain).proceed(isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class)); removeCache(); reset(mockExecChain); when(mockExecChain.proceed(isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class))).thenReturn(response); final ClassicHttpResponse response2 = t.execute(get, scope, mockExecChain); Assertions.assertEquals(200, response2.getCode()); EntityUtils.consume(response2.getEntity()); verify(mockExecChain, Mockito.times(1)).proceed( isA(ClassicHttpRequest.class), isA(ExecChain.Scope.class)); Assertions.assertEquals(CacheResponseStatus.FAILURE, context.getCacheResponseStatus()); } protected ExecChainHandler createCachingExecChain(final BasicHttpCache cache, final CacheConfig config) { return new CachingExec(cache, null, config); } } TestProtocolAllowedBehavior.java000066400000000000000000000130141434266521000421700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.net.SocketTimeoutException; import java.time.Instant; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** * This class tests behavior that is allowed (MAY) by the HTTP/1.1 protocol * specification and for which we have implemented the behavior in HTTP cache. */ public class TestProtocolAllowedBehavior { static final int MAX_BYTES = 1024; static final int MAX_ENTRIES = 100; static final int ENTITY_LENGTH = 128; HttpHost host; HttpRoute route; HttpClientContext context; @Mock ExecChain mockExecChain; @Mock ExecRuntime mockExecRuntime; @Mock HttpCache mockCache; ClassicHttpRequest request; ClassicHttpResponse originResponse; CacheConfig config; CachingExec impl; HttpCache cache; @BeforeEach public void setUp() throws Exception { MockitoAnnotations.openMocks(this); host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); request = new BasicClassicHttpRequest("GET", "/foo"); context = HttpClientContext.create(); originResponse = HttpTestUtils.make200Response(); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .setSharedCache(false) .build(); cache = new BasicHttpCache(config); impl = new CachingExec(cache, null, config); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); } public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException { return impl.execute( ClassicRequestBuilder.copy(request).build(), new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); } @Test public void testNonSharedCacheReturnsStaleResponseWhenRevalidationFailsForProxyRevalidate() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/"); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); originResponse.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); originResponse.setHeader("Cache-Control","max-age=5,proxy-revalidate"); originResponse.setHeader("Etag","\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new SocketTimeoutException()); final HttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_OK, result.getCode()); Mockito.verifyNoInteractions(mockCache); } @Test public void testNonSharedCacheMayCacheResponsesWithCacheControlPrivate() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/"); originResponse.setHeader("Cache-Control","private,max-age=3600"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final HttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_OK, result.getCode()); Mockito.verifyNoInteractions(mockCache); } } TestProtocolDeviations.java000066400000000000000000000205521434266521000412330ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.time.Instant; import java.util.Random; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.cache.HttpCacheContext; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** * 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. * * 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. */ @SuppressWarnings("boxing") // test code public class TestProtocolDeviations { private static final int MAX_BYTES = 1024; private static final int MAX_ENTRIES = 100; HttpHost host; HttpRoute route; @Mock ExecRuntime mockEndpoint; @Mock ExecChain mockExecChain; ClassicHttpRequest request; HttpCacheContext context; ClassicHttpResponse originResponse; ExecChainHandler impl; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); request = new BasicClassicHttpRequest("GET", "/foo"); context = HttpCacheContext.create(); originResponse = make200Response(); final CacheConfig config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .build(); final HttpCache cache = new BasicHttpCache(config); impl = createCachingExecChain(cache, config); } private ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException { return impl.execute(request, new ExecChain.Scope("test", route, request, mockEndpoint, context), mockExecChain); } protected ExecChainHandler createCachingExecChain(final HttpCache cache, final CacheConfig config) { return new CachingExec(cache, null, config); } private ClassicHttpResponse make200Response() { final ClassicHttpResponse out = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); out.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); out.setHeader("Server", "MockOrigin/1.0"); out.setEntity(makeBody(128)); return out; } private HttpEntity makeBody(final int nbytes) { final byte[] bytes = new byte[nbytes]; new Random().nextBytes(bytes); return new ByteArrayEntity(bytes, null); } /* * "10.2.7 206 Partial Content ... The request MUST have included a Range * header field (section 14.35) indicating the desired range, and MAY have * included an If-Range header field (section 14.27) to make the request * conditional." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 */ @Test public void testPartialContentIsNotReturnedToAClientThatDidNotAskForIt() throws Exception { // tester's note: I don't know what the cache will *do* in // this situation, but it better not just pass the response // on. request.removeHeaders("Range"); originResponse = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); originResponse.setHeader("Content-Range", "bytes 0-499/1234"); originResponse.setEntity(makeBody(500)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); try { final HttpResponse result = execute(request); Assertions.assertTrue(HttpStatus.SC_PARTIAL_CONTENT != result.getCode()); } catch (final ClientProtocolException acceptableBehavior) { // this is probably ok } } /* * "10.4.2 401 Unauthorized ... The response MUST include a WWW-Authenticate * header field (section 14.47) containing a challenge applicable to the * requested resource." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 */ @Test public void testPassesOnOrigin401ResponseWithoutWWWAuthenticateHeader() throws Exception { originResponse = new BasicClassicHttpResponse(401, "Unauthorized"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final HttpResponse result = execute(request); Assertions.assertSame(originResponse, result); } /* * "10.4.6 405 Method Not Allowed ... The response MUST include an Allow * header containing a list of valid methods for the requested resource. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 */ @Test public void testPassesOnOrigin405WithoutAllowHeader() throws Exception { originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final HttpResponse result = execute(request); Assertions.assertSame(originResponse, result); } /* * "10.4.8 407 Proxy Authentication Required ... The proxy MUST return a * Proxy-Authenticate header field (section 14.33) containing a challenge * applicable to the proxy for the requested resource." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.8 */ @Test public void testPassesOnOrigin407WithoutAProxyAuthenticateHeader() throws Exception { originResponse = new BasicClassicHttpResponse(407, "Proxy Authentication Required"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final HttpResponse result = execute(request); Assertions.assertSame(originResponse, result); } } TestProtocolRecommendations.java000066400000000000000000002106141434266521000422550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.MessageSupport; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /* * This test class captures functionality required to achieve unconditional * compliance with the HTTP/1.1 spec, i.e. all the SHOULD, SHOULD NOT, * RECOMMENDED, and NOT RECOMMENDED behaviors. */ public class TestProtocolRecommendations { static final int MAX_BYTES = 1024; static final int MAX_ENTRIES = 100; static final int ENTITY_LENGTH = 128; HttpHost host; HttpRoute route; HttpEntity body; HttpClientContext context; @Mock ExecChain mockExecChain; @Mock ExecRuntime mockExecRuntime; ClassicHttpRequest request; ClassicHttpResponse originResponse; CacheConfig config; CachingExec impl; HttpCache cache; Instant now; Instant tenSecondsAgo; Instant twoMinutesAgo; @BeforeEach public void setUp() throws Exception { MockitoAnnotations.openMocks(this); host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); body = HttpTestUtils.makeBody(ENTITY_LENGTH); request = new BasicClassicHttpRequest("GET", "/foo"); context = HttpClientContext.create(); originResponse = HttpTestUtils.make200Response(); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .build(); cache = new BasicHttpCache(config); impl = new CachingExec(cache, null, config); now = Instant.now(); tenSecondsAgo = now.minus(10, ChronoUnit.SECONDS); twoMinutesAgo = now.minus(1, ChronoUnit.MINUTES); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); } public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException { return impl.execute( ClassicRequestBuilder.copy(request).build(), new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); } /* "identity: The default (identity) encoding; the use of no * transformation whatsoever. This content-coding is used only in the * Accept-Encoding header, and SHOULD NOT be used in the * Content-Encoding header." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5 */ @Test public void testIdentityCodingIsNotUsedInContentEncodingHeader() throws Exception { originResponse.setHeader("Content-Encoding", "identity"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); boolean foundIdentity = false; final Iterator it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("identity".equalsIgnoreCase(elt.getName())) { foundIdentity = true; } } assertFalse(foundIdentity); } /* * "304 Not Modified. ... If the conditional GET used a strong cache * validator (see section 13.3.3), the response SHOULD NOT include * other entity-headers." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 */ private void cacheGenerated304ForValidatorShouldNotContainEntityHeader( final String headerName, final String headerValue, final String validatorHeader, final String validator, final String conditionalHeader) throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader(validatorHeader, validator); resp1.setHeader(headerName, headerValue); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader(conditionalHeader, validator); execute(req1); final ClassicHttpResponse result = execute(req2); if (HttpStatus.SC_NOT_MODIFIED == result.getCode()) { assertNull(result.getFirstHeader(headerName)); } } private void cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( final String headerName, final String headerValue) throws Exception { cacheGenerated304ForValidatorShouldNotContainEntityHeader(headerName, headerValue, "ETag", "\"etag\"", "If-None-Match"); } private void cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( final String headerName, final String headerValue) throws Exception { cacheGenerated304ForValidatorShouldNotContainEntityHeader(headerName, headerValue, "Last-Modified", DateUtils.formatStandardDate(twoMinutesAgo), "If-Modified-Since"); } @Test public void cacheGenerated304ForStrongEtagValidatorShouldNotContainAllow() throws Exception { cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( "Allow", "GET,HEAD"); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainAllow() throws Exception { cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( "Allow", "GET,HEAD"); } @Test public void cacheGenerated304ForStrongEtagValidatorShouldNotContainContentEncoding() throws Exception { cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( "Content-Encoding", "gzip"); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentEncoding() throws Exception { cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( "Content-Encoding", "gzip"); } @Test public void cacheGenerated304ForStrongEtagValidatorShouldNotContainContentLanguage() throws Exception { cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( "Content-Language", "en"); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentLanguage() throws Exception { cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( "Content-Language", "en"); } @Test public void cacheGenerated304ForStrongValidatorShouldNotContainContentLength() throws Exception { cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( "Content-Length", "128"); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentLength() throws Exception { cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( "Content-Length", "128"); } @Test public void cacheGenerated304ForStrongValidatorShouldNotContainContentMD5() throws Exception { cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( "Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentMD5() throws Exception { cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( "Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); } private void cacheGenerated304ForStrongValidatorShouldNotContainContentRange( final String validatorHeader, final String validator, final String conditionalHeader) throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); req1.setHeader("Range","bytes=0-127"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader(validatorHeader, validator); resp1.setHeader("Content-Range", "bytes 0-127/256"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("If-Range", validator); req2.setHeader("Range","bytes=0-127"); req2.setHeader(conditionalHeader, validator); try (final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified")) { resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader(validatorHeader, validator); } // cache module does not currently deal with byte ranges, but we want // this test to work even if it does some day execute(req1); final ClassicHttpResponse result = execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); if (allRequests.isEmpty() && HttpStatus.SC_NOT_MODIFIED == result.getCode()) { // cache generated a 304 assertNull(result.getFirstHeader("Content-Range")); } } @Test public void cacheGenerated304ForStrongEtagValidatorShouldNotContainContentRange() throws Exception { cacheGenerated304ForStrongValidatorShouldNotContainContentRange( "ETag", "\"etag\"", "If-None-Match"); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentRange() throws Exception { cacheGenerated304ForStrongValidatorShouldNotContainContentRange( "Last-Modified", DateUtils.formatStandardDate(twoMinutesAgo), "If-Modified-Since"); } @Test public void cacheGenerated304ForStrongEtagValidatorShouldNotContainContentType() throws Exception { cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( "Content-Type", "text/html"); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentType() throws Exception { cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( "Content-Type", "text/html"); } @Test public void cacheGenerated304ForStrongEtagValidatorShouldNotContainLastModified() throws Exception { cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader( "Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); } @Test public void cacheGenerated304ForStrongDateValidatorShouldNotContainLastModified() throws Exception { cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader( "Last-Modified", DateUtils.formatStandardDate(twoMinutesAgo)); } private void shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( final String entityHeader, final String entityHeaderValue) throws Exception { final ClassicHttpRequest req = HttpTestUtils.makeDefaultRequest(); req.setHeader("If-None-Match", "\"etag\""); final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp.setHeader("Date", DateUtils.formatStandardDate(now)); resp.setHeader("Etag", "\"etag\""); resp.setHeader(entityHeader, entityHeaderValue); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp); final ClassicHttpResponse result = execute(req); assertNull(result.getFirstHeader(entityHeader)); } @Test public void shouldStripAllowFromOrigin304ResponseToStrongValidation() throws Exception { shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( "Allow", "GET,HEAD"); } @Test public void shouldStripContentEncodingFromOrigin304ResponseToStrongValidation() throws Exception { shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( "Content-Encoding", "gzip"); } @Test public void shouldStripContentLanguageFromOrigin304ResponseToStrongValidation() throws Exception { shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( "Content-Language", "en"); } @Test public void shouldStripContentLengthFromOrigin304ResponseToStrongValidation() throws Exception { shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( "Content-Length", "128"); } @Test public void shouldStripContentMD5FromOrigin304ResponseToStrongValidation() throws Exception { shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( "Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); } @Test public void shouldStripContentTypeFromOrigin304ResponseToStrongValidation() throws Exception { shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( "Content-Type", "text/html;charset=utf-8"); } @Test public void shouldStripContentRangeFromOrigin304ResponseToStringValidation() throws Exception { final ClassicHttpRequest req = HttpTestUtils.makeDefaultRequest(); req.setHeader("If-Range","\"etag\""); req.setHeader("Range","bytes=0-127"); final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp.setHeader("Date", DateUtils.formatStandardDate(now)); resp.setHeader("ETag", "\"etag\""); resp.setHeader("Content-Range", "bytes 0-127/256"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp); final ClassicHttpResponse result = execute(req); assertNull(result.getFirstHeader("Content-Range")); } @Test public void shouldStripLastModifiedFromOrigin304ResponseToStrongValidation() throws Exception { shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation( "Last-Modified", DateUtils.formatStandardDate(twoMinutesAgo)); } /* * "For this reason, a cache SHOULD NOT return a stale response if the * client explicitly requests a first-hand or fresh one, unless it is * impossible to comply for technical or policy reasons." */ private ClassicHttpRequest requestToPopulateStaleCacheEntry() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control","public,max-age=5"); resp1.setHeader("Etag","\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); return req1; } private void testDoesNotReturnStaleResponseOnError(final ClassicHttpRequest req2) throws Exception { final ClassicHttpRequest req1 = requestToPopulateStaleCacheEntry(); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException()); ClassicHttpResponse result = null; try { result = execute(req2); } catch (final IOException acceptable) { } if (result != null) { assertNotEquals(HttpStatus.SC_OK, result.getCode()); } } @Test public void testDoesNotReturnStaleResponseIfClientExplicitlyRequestsFirstHandOneWithCacheControl() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Cache-Control","no-cache"); testDoesNotReturnStaleResponseOnError(req); } @Test public void testDoesNotReturnStaleResponseIfClientExplicitlyRequestsFirstHandOneWithPragma() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Pragma","no-cache"); testDoesNotReturnStaleResponseOnError(req); } @Test public void testDoesNotReturnStaleResponseIfClientExplicitlyRequestsFreshWithMaxAge() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Cache-Control","max-age=0"); testDoesNotReturnStaleResponseOnError(req); } @Test public void testDoesNotReturnStaleResponseIfClientExplicitlySpecifiesLargerMaxAge() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Cache-Control","max-age=20"); testDoesNotReturnStaleResponseOnError(req); } @Test public void testDoesNotReturnStaleResponseIfClientExplicitlyRequestsFreshWithMinFresh() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Cache-Control","min-fresh=2"); testDoesNotReturnStaleResponseOnError(req); } @Test public void testDoesNotReturnStaleResponseIfClientExplicitlyRequestsFreshWithMaxStale() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Cache-Control","max-stale=2"); testDoesNotReturnStaleResponseOnError(req); } @Test public void testMayReturnStaleResponseIfClientExplicitlySpecifiesAcceptableMaxStale() throws Exception { final ClassicHttpRequest req1 = requestToPopulateStaleCacheEntry(); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control","max-stale=20"); execute(req1); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_OK, result.getCode()); assertNotNull(result.getFirstHeader("Warning")); Mockito.verify(mockExecChain, Mockito.atMost(1)).proceed(Mockito.any(), Mockito.any()); } /* * "A correct cache MUST respond to a request with the most up-to-date * response held by the cache that is appropriate to the request * (see sections 13.2.5, 13.2.6, and 13.12) which meets one of the * following conditions: * * 1. It has been checked for equivalence with what the origin server * would have returned by revalidating the response with the * origin server (section 13.3); * * 2. It is "fresh enough" (see section 13.2). In the default case, * this means it meets the least restrictive freshness requirement * of the client, origin server, and cache (see section 14.9); if * the origin server so specifies, it is the freshness requirement * of the origin server alone. * * If a stored response is not "fresh enough" by the most * restrictive freshness requirement of both the client and the * origin server, in carefully considered circumstances the cache * MAY still return the response with the appropriate Warning * header (see section 13.1.5 and 14.46), unless such a response * is prohibited (e.g., by a "no-store" cache-directive, or by a * "no-cache" cache-request-directive; see section 14.9). * * 3. It is an appropriate 304 (Not Modified), 305 (Proxy Redirect), * or error (4xx or 5xx) response message. * * If the cache can not communicate with the origin server, then a * correct cache SHOULD respond as above if the response can be * correctly served from the cache..." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.1.1 */ @Test public void testReturnsCachedResponsesAppropriatelyWhenNoOriginCommunication() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "public, max-age=5"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new IOException()); final ClassicHttpResponse result = execute(req2); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); assertEquals(HttpStatus.SC_OK, result.getCode()); boolean warning111Found = false; for(final Header h : result.getHeaders("Warning")) { for(final WarningValue wv : WarningValue.getWarningValues(h)) { if (wv.getWarnCode() == 111) { warning111Found = true; break; } } } assertTrue(warning111Found); } /* * "If a cache receives a response (either an entire response, or a * 304 (Not Modified) response) that it would normally forward to the * requesting client, and the received response is no longer fresh, * the cache SHOULD forward it to the requesting client without adding * a new Warning (but without removing any existing Warning headers). * A cache SHOULD NOT attempt to revalidate a response simply because * that response became stale in transit; this might lead to an * infinite loop." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.1.1 */ @Test public void testDoesNotAddNewWarningHeaderIfResponseArrivesStale() throws Exception { originResponse.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); originResponse.setHeader("Cache-Control","public, max-age=5"); originResponse.setHeader("ETag","\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); assertNull(result.getFirstHeader("Warning")); } @Test public void testForwardsExistingWarningHeadersOnResponseThatArrivesStale() throws Exception { originResponse.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); originResponse.setHeader("Cache-Control","public, max-age=5"); originResponse.setHeader("ETag","\"etag\""); originResponse.addHeader("Age","10"); final String warning = "110 fred \"Response is stale\""; originResponse.addHeader("Warning",warning); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); assertEquals(warning, result.getFirstHeader("Warning").getValue()); } /* * "A transparent proxy SHOULD NOT modify an end-to-end header unless * the definition of that header requires or specifically allows that." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.2 */ private void testDoesNotModifyHeaderOnResponses(final String headerName) throws Exception { final String headerValue = HttpTestUtils .getCanonicalHeaderValue(originResponse, headerName); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); assertEquals(headerValue, result.getFirstHeader(headerName).getValue()); } private void testDoesNotModifyHeaderOnRequests(final String headerName) throws Exception { final String headerValue = HttpTestUtils.getCanonicalHeaderValue(request, headerName); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); assertEquals(headerValue, HttpTestUtils.getCanonicalHeaderValue(reqCapture.getValue(), headerName)); } @Test public void testDoesNotModifyAcceptRangesOnResponses() throws Exception { final String headerName = "Accept-Ranges"; originResponse.setHeader(headerName,"bytes"); testDoesNotModifyHeaderOnResponses(headerName); } @Test public void testDoesNotModifyAuthorizationOnRequests() throws Exception { request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); testDoesNotModifyHeaderOnRequests("Authorization"); } @Test public void testDoesNotModifyContentLengthOnRequests() throws Exception { final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setEntity(HttpTestUtils.makeBody(128)); post.setHeader("Content-Length","128"); request = post; testDoesNotModifyHeaderOnRequests("Content-Length"); } @Test public void testDoesNotModifyContentLengthOnResponses() throws Exception { originResponse.setEntity(HttpTestUtils.makeBody(128)); originResponse.setHeader("Content-Length","128"); testDoesNotModifyHeaderOnResponses("Content-Length"); } @Test public void testDoesNotModifyContentMD5OnRequests() throws Exception { final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setEntity(HttpTestUtils.makeBody(128)); post.setHeader("Content-Length","128"); post.setHeader("Content-MD5","Q2hlY2sgSW50ZWdyaXR5IQ=="); request = post; testDoesNotModifyHeaderOnRequests("Content-MD5"); } @Test public void testDoesNotModifyContentMD5OnResponses() throws Exception { originResponse.setEntity(HttpTestUtils.makeBody(128)); originResponse.setHeader("Content-MD5","Q2hlY2sgSW50ZWdyaXR5IQ=="); testDoesNotModifyHeaderOnResponses("Content-MD5"); } @Test public void testDoesNotModifyContentRangeOnRequests() throws Exception { final ClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/"); put.setEntity(HttpTestUtils.makeBody(128)); put.setHeader("Content-Length","128"); put.setHeader("Content-Range","bytes 0-127/256"); request = put; testDoesNotModifyHeaderOnRequests("Content-Range"); } @Test public void testDoesNotModifyContentRangeOnResponses() throws Exception { request.setHeader("Range","bytes=0-128"); originResponse.setCode(HttpStatus.SC_PARTIAL_CONTENT); originResponse.setReasonPhrase("Partial Content"); originResponse.setEntity(HttpTestUtils.makeBody(128)); originResponse.setHeader("Content-Range","bytes 0-127/256"); testDoesNotModifyHeaderOnResponses("Content-Range"); } @Test public void testDoesNotModifyContentTypeOnRequests() throws Exception { final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setEntity(HttpTestUtils.makeBody(128)); post.setHeader("Content-Length","128"); post.setHeader("Content-Type","application/octet-stream"); request = post; testDoesNotModifyHeaderOnRequests("Content-Type"); } @Test public void testDoesNotModifyContentTypeOnResponses() throws Exception { originResponse.setHeader("Content-Type","application/octet-stream"); testDoesNotModifyHeaderOnResponses("Content-Type"); } @Test public void testDoesNotModifyDateOnRequests() throws Exception { request.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); testDoesNotModifyHeaderOnRequests("Date"); } @Test public void testDoesNotModifyDateOnResponses() throws Exception { originResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); testDoesNotModifyHeaderOnResponses("Date"); } @Test public void testDoesNotModifyETagOnResponses() throws Exception { originResponse.setHeader("ETag", "\"random-etag\""); testDoesNotModifyHeaderOnResponses("ETag"); } @Test public void testDoesNotModifyExpiresOnResponses() throws Exception { originResponse.setHeader("Expires", DateUtils.formatStandardDate(Instant.now())); testDoesNotModifyHeaderOnResponses("Expires"); } @Test public void testDoesNotModifyFromOnRequests() throws Exception { request.setHeader("From", "foo@example.com"); testDoesNotModifyHeaderOnRequests("From"); } @Test public void testDoesNotModifyIfMatchOnRequests() throws Exception { request = new BasicClassicHttpRequest("DELETE", "/"); request.setHeader("If-Match", "\"etag\""); testDoesNotModifyHeaderOnRequests("If-Match"); } @Test public void testDoesNotModifyIfModifiedSinceOnRequests() throws Exception { request.setHeader("If-Modified-Since", DateUtils.formatStandardDate(Instant.now())); testDoesNotModifyHeaderOnRequests("If-Modified-Since"); } @Test public void testDoesNotModifyIfNoneMatchOnRequests() throws Exception { request.setHeader("If-None-Match", "\"etag\""); testDoesNotModifyHeaderOnRequests("If-None-Match"); } @Test public void testDoesNotModifyIfRangeOnRequests() throws Exception { request.setHeader("Range","bytes=0-128"); request.setHeader("If-Range", "\"etag\""); testDoesNotModifyHeaderOnRequests("If-Range"); } @Test public void testDoesNotModifyIfUnmodifiedSinceOnRequests() throws Exception { request = new BasicClassicHttpRequest("DELETE", "/"); request.setHeader("If-Unmodified-Since", DateUtils.formatStandardDate(Instant.now())); testDoesNotModifyHeaderOnRequests("If-Unmodified-Since"); } @Test public void testDoesNotModifyLastModifiedOnResponses() throws Exception { originResponse.setHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now())); testDoesNotModifyHeaderOnResponses("Last-Modified"); } @Test public void testDoesNotModifyLocationOnResponses() throws Exception { originResponse.setCode(HttpStatus.SC_TEMPORARY_REDIRECT); originResponse.setReasonPhrase("Temporary Redirect"); originResponse.setHeader("Location", "http://foo.example.com/bar"); testDoesNotModifyHeaderOnResponses("Location"); } @Test public void testDoesNotModifyRangeOnRequests() throws Exception { request.setHeader("Range", "bytes=0-128"); testDoesNotModifyHeaderOnRequests("Range"); } @Test public void testDoesNotModifyRefererOnRequests() throws Exception { request.setHeader("Referer", "http://foo.example.com/bar"); testDoesNotModifyHeaderOnRequests("Referer"); } @Test public void testDoesNotModifyRetryAfterOnResponses() throws Exception { originResponse.setCode(HttpStatus.SC_SERVICE_UNAVAILABLE); originResponse.setReasonPhrase("Service Unavailable"); originResponse.setHeader("Retry-After", "120"); testDoesNotModifyHeaderOnResponses("Retry-After"); } @Test public void testDoesNotModifyServerOnResponses() throws Exception { originResponse.setHeader("Server", "SomeServer/1.0"); testDoesNotModifyHeaderOnResponses("Server"); } @Test public void testDoesNotModifyUserAgentOnRequests() throws Exception { request.setHeader("User-Agent", "MyClient/1.0"); testDoesNotModifyHeaderOnRequests("User-Agent"); } @Test public void testDoesNotModifyVaryOnResponses() throws Exception { request.setHeader("Accept-Encoding","identity"); originResponse.setHeader("Vary", "Accept-Encoding"); testDoesNotModifyHeaderOnResponses("Vary"); } @Test public void testDoesNotModifyExtensionHeaderOnRequests() throws Exception { request.setHeader("X-Extension","x-value"); testDoesNotModifyHeaderOnRequests("X-Extension"); } @Test public void testDoesNotModifyExtensionHeaderOnResponses() throws Exception { originResponse.setHeader("X-Extension", "x-value"); testDoesNotModifyHeaderOnResponses("X-Extension"); } /* * "[HTTP/1.1 clients], If only a Last-Modified value has been provided * by the origin server, SHOULD use that value in non-subrange cache- * conditional requests (using If-Modified-Since)." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 */ @Test public void testUsesLastModifiedDateForCacheConditionalRequests() throws Exception { final Instant twentySecondsAgo = now.plusSeconds(20); final String lmDate = DateUtils.formatStandardDate(twentySecondsAgo); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Last-Modified", lmDate); resp1.setHeader("Cache-Control","max-age=5"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); final Header ifModifiedSince = captured.getFirstHeader("If-Modified-Since"); assertEquals(lmDate, ifModifiedSince.getValue()); } /* * "[HTTP/1.1 clients], if both an entity tag and a Last-Modified value * have been provided by the origin server, SHOULD use both validators * in cache-conditional requests. This allows both HTTP/1.0 and * HTTP/1.1 caches to respond appropriately." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 */ @Test public void testUsesBothLastModifiedAndETagForConditionalRequestsIfAvailable() throws Exception { final Instant twentySecondsAgo = now.plusSeconds(20); final String lmDate = DateUtils.formatStandardDate(twentySecondsAgo); final String etag = "\"etag\""; final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Last-Modified", lmDate); resp1.setHeader("Cache-Control","max-age=5"); resp1.setHeader("ETag", etag); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); final Header ifModifiedSince = captured.getFirstHeader("If-Modified-Since"); assertEquals(lmDate, ifModifiedSince.getValue()); final Header ifNoneMatch = captured.getFirstHeader("If-None-Match"); assertEquals(etag, ifNoneMatch.getValue()); } /* * "If an origin server wishes to force a semantically transparent cache * to validate every request, it MAY assign an explicit expiration time * in the past. This means that the response is always stale, and so the * cache SHOULD validate it before using it for subsequent requests." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.1 */ @Test public void testRevalidatesCachedResponseWithExpirationInThePast() throws Exception { final Instant oneSecondAgo = now.minusSeconds(1); final Instant oneSecondFromNow = now.plusSeconds(1); final Instant twoSecondsFromNow = now.plusSeconds(2); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Expires",DateUtils.formatStandardDate(oneSecondAgo)); resp1.setHeader("Cache-Control", "public"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest revalidate = new BasicClassicHttpRequest("GET", "/"); revalidate.setHeader("If-None-Match","\"etag\""); final ClassicHttpResponse resp2 = HttpTestUtils.make304Response(); resp2.setHeader("Date", DateUtils.formatStandardDate(twoSecondsFromNow)); resp2.setHeader("Expires", DateUtils.formatStandardDate(oneSecondFromNow)); resp2.setHeader("ETag","\"etag\""); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(revalidate), Mockito.any())).thenReturn(resp2); execute(req1); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_OK, result.getCode()); } /* "When a client tries to revalidate a cache entry, and the response * it receives contains a Date header that appears to be older than the * one for the existing entry, then the client SHOULD repeat the * request unconditionally, and include * Cache-Control: max-age=0 * to force any intermediate caches to validate their copies directly * with the origin server, or * Cache-Control: no-cache * to force any intermediate caches to obtain a new copy from the * origin server." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.6 */ @Test public void testRetriesValidationThatResultsInAnOlderDated304Response() throws Exception { final Instant elevenSecondsAgo = now.minusSeconds(11); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control","max-age=5"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make304Response(); resp2.setHeader("ETag","\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(elevenSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); boolean hasMaxAge0 = false; boolean hasNoCache = false; final Iterator it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("max-age".equals(elt.getName())) { try { final int maxage = Integer.parseInt(elt.getValue()); if (maxage == 0) { hasMaxAge0 = true; } } catch (final NumberFormatException nfe) { // nop } } else if ("no-cache".equals(elt.getName())) { hasNoCache = true; } } assertTrue(hasMaxAge0 || hasNoCache); assertNull(captured.getFirstHeader("If-None-Match")); assertNull(captured.getFirstHeader("If-Modified-Since")); assertNull(captured.getFirstHeader("If-Range")); assertNull(captured.getFirstHeader("If-Match")); assertNull(captured.getFirstHeader("If-Unmodified-Since")); } /* "If an entity tag was assigned to a cached representation, the * forwarded request SHOULD be conditional and include the entity * tags in an If-None-Match header field from all its cache entries * for the resource." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 */ @Test public void testSendsAllVariantEtagsInConditionalRequest() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/"); req1.setHeader("User-Agent","agent1"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Vary","User-Agent"); resp1.setHeader("Etag","\"etag1\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/"); req2.setHeader("User-Agent","agent2"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Vary","User-Agent"); resp2.setHeader("Etag","\"etag2\""); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET","/"); req3.setHeader("User-Agent","agent3"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); Mockito.when(mockExecChain.proceed(Mockito.any(),Mockito.any())).thenReturn(resp3); execute(req3); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); boolean foundEtag1 = false; boolean foundEtag2 = false; for(final Header h : captured.getHeaders("If-None-Match")) { for(final String etag : h.getValue().split(",")) { if ("\"etag1\"".equals(etag.trim())) { foundEtag1 = true; } if ("\"etag2\"".equals(etag.trim())) { foundEtag2 = true; } } } assertTrue(foundEtag1 && foundEtag2); } /* "If the entity-tag of the new response matches that of an existing * entry, the new response SHOULD be used to processChallenge the header fields * of the existing entry, and the result MUST be returned to the * client." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 */ @Test public void testResponseToExistingVariantsUpdatesEntry() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("User-Agent", "agent1"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Vary", "User-Agent"); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("ETag", "\"etag1\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("User-Agent", "agent2"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp2.setHeader("Vary", "User-Agent"); resp2.setHeader("Cache-Control", "max-age=3600"); resp2.setHeader("ETag", "\"etag2\""); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("User-Agent", "agent3"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); resp3.setHeader("ETag", "\"etag1\""); final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/"); req4.setHeader("User-Agent", "agent1"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); final ClassicHttpResponse result1 = execute(req3); final ClassicHttpResponse result2 = execute(req4); assertEquals(HttpStatus.SC_OK, result1.getCode()); assertEquals("\"etag1\"", result1.getFirstHeader("ETag").getValue()); assertEquals(DateUtils.formatStandardDate(now), result1.getFirstHeader("Date").getValue()); assertEquals(DateUtils.formatStandardDate(now), result2.getFirstHeader("Date").getValue()); } @Test public void testResponseToExistingVariantsIsCachedForFutureResponses() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("User-Agent", "agent1"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Vary", "User-Agent"); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("ETag", "\"etag1\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("User-Agent", "agent2"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("ETag", "\"etag1\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("User-Agent", "agent2"); execute(req1); execute(req2); execute(req3); } /* "If any of the existing cache entries contains only partial content * for the associated entity, its entity-tag SHOULD NOT be included in * the If-None-Match header field unless the request is for a range * that would be fully satisfied by that entry." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 */ @Test public void variantNegotiationsDoNotIncludeEtagsForPartialResponses() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); req1.setHeader("User-Agent", "agent1"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Vary", "User-Agent"); resp1.setHeader("ETag", "\"etag1\""); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("User-Agent", "agent2"); req2.setHeader("Range", "bytes=0-49"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(50)); resp2.setHeader("Content-Length","50"); resp2.setHeader("Content-Range","bytes 0-49/100"); resp2.setHeader("Vary","User-Agent"); resp2.setHeader("ETag", "\"etag2\""); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest(); req3.setHeader("User-Agent", "agent3"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Vary", "User-Agent"); resp1.setHeader("ETag", "\"etag3\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req3); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(3)).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); final Iterator it = MessageSupport.iterate(captured, HttpHeaders.IF_NONE_MATCH); while (it.hasNext()) { final HeaderElement elt = it.next(); assertNotEquals("\"etag2\"", elt.toString()); } } /* "If a cache receives a successful response whose Content-Location * field matches that of an existing cache entry for the same Request- * URI, whose entity-tag differs from that of the existing entry, and * whose Date is more recent than that of the existing entry, the * existing entry SHOULD NOT be returned in response to future requests * and SHOULD be deleted from the cache. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 */ @Test public void cachedEntryShouldNotBeUsedIfMoreRecentMentionInContentLocation() throws Exception { final ClassicHttpRequest req1 = new HttpGet("http://foo.example.com/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("ETag", "\"old-etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new HttpPost("http://foo.example.com/bar"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag", "\"new-etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Content-Location", "http://foo.example.com/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new HttpGet("http://foo.example.com"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } /* * "This specifically means that responses from HTTP/1.0 servers for such * URIs [those containing a '?' in the rel_path part] SHOULD NOT be taken * from a cache." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9 */ @Test public void responseToGetWithQueryFrom1_0OriginAndNoExpiresIsNotCached() throws Exception { final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/bar?baz=quux"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setVersion(HttpVersion.HTTP_1_0); resp2.setEntity(HttpTestUtils.makeBody(200)); resp2.setHeader("Content-Length","200"); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); } @Test public void responseToGetWithQueryFrom1_0OriginVia1_1ProxyAndNoExpiresIsNotCached() throws Exception { final ClassicHttpRequest req2 = new HttpGet("http://foo.example.com/bar?baz=quux"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp2.setVersion(HttpVersion.HTTP_1_0); resp2.setEntity(HttpTestUtils.makeBody(200)); resp2.setHeader("Content-Length","200"); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Via","1.0 someproxy"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); } /* * "A cache that passes through requests for methods it does not * understand SHOULD invalidate any entities referred to by the * Request-URI." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10 */ @Test public void shouldInvalidateNonvariantCacheEntryForUnknownMethod() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("FROB", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control","max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); resp3.setHeader("ETag", "\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); final ClassicHttpResponse result = execute(req3); assertTrue(HttpTestUtils.semanticallyTransparent(resp3, result)); } @Test public void shouldInvalidateAllVariantsForUnknownMethod() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("User-Agent", "agent1"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Vary", "User-Agent"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("User-Agent", "agent2"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Vary", "User-Agent"); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("FROB", "/"); req3.setHeader("User-Agent", "agent3"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); resp3.setHeader("Cache-Control","max-age=3600"); final ClassicHttpRequest req4 = new BasicClassicHttpRequest("GET", "/"); req4.setHeader("User-Agent", "agent1"); final ClassicHttpResponse resp4 = HttpTestUtils.make200Response(); resp4.setHeader("ETag", "\"etag1\""); final ClassicHttpRequest req5 = new BasicClassicHttpRequest("GET", "/"); req5.setHeader("User-Agent", "agent2"); final ClassicHttpResponse resp5 = HttpTestUtils.make200Response(); resp5.setHeader("ETag", "\"etag2\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req3); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp4); final ClassicHttpResponse result4 = execute(req4); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp5); final ClassicHttpResponse result5 = execute(req5); assertTrue(HttpTestUtils.semanticallyTransparent(resp4, result4)); assertTrue(HttpTestUtils.semanticallyTransparent(resp5, result5)); } /* * "If a new cacheable response is received from a resource while any * existing responses for the same resource are cached, the cache * SHOULD use the new response to reply to the current request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.12 */ @Test public void cacheShouldUpdateWithNewCacheableResponse() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("ETag", "\"etag1\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "max-age=0"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Cache-Control", "max-age=3600"); resp2.setHeader("ETag", "\"etag2\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = HttpTestUtils.makeDefaultRequest(); execute(req1); execute(req2); final ClassicHttpResponse result = execute(req3); assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result)); } /* * "Many HTTP/1.0 cache implementations will treat an Expires value * that is less than or equal to the response Date value as being * equivalent to the Cache-Control response directive 'no-cache'. * If an HTTP/1.1 cache receives such a response, and the response * does not include a Cache-Control header field, it SHOULD consider * the response to be non-cacheable in order to retain compatibility * with HTTP/1.0 servers." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 */ @Test public void expiresEqualToDateWithNoCacheControlIsNotCacheable() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Expires", DateUtils.formatStandardDate(now)); resp1.removeHeaders("Cache-Control"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "max-stale=1000"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag", "\"etag2\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); final ClassicHttpResponse result = execute(req2); assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result)); } @Test public void expiresPriorToDateWithNoCacheControlIsNotCacheable() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.removeHeaders("Cache-Control"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "max-stale=1000"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag", "\"etag2\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); final ClassicHttpResponse result = execute(req2); assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result)); } /* * "If a request includes the no-cache directive, it SHOULD NOT * include min-fresh, max-stale, or max-age." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4 */ @Test public void otherFreshnessRequestDirectivesNotAllowedWithNoCache() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); req1.setHeader("Cache-Control", "min-fresh=10, no-cache"); req1.addHeader("Cache-Control", "max-stale=0, max-age=0"); execute(req1); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); boolean foundNoCache = false; boolean foundDisallowedDirective = false; final List disallowed = Arrays.asList("min-fresh", "max-stale", "max-age"); final Iterator it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if (disallowed.contains(elt.getName())) { foundDisallowedDirective = true; } if ("no-cache".equals(elt.getName())) { foundNoCache = true; } } assertTrue(foundNoCache); assertFalse(foundDisallowedDirective); } /* * "To do this, the client may include the only-if-cached directive in * a request. If it receives this directive, a cache SHOULD either * respond using a cached entry that is consistent with the other * constraints of the request, or respond with a 504 (Gateway Timeout) * status." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4 */ @Test public void cacheMissResultsIn504WithOnlyIfCached() throws Exception { final ClassicHttpRequest req = HttpTestUtils.makeDefaultRequest(); req.setHeader("Cache-Control", "only-if-cached"); final ClassicHttpResponse result = execute(req); assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, result.getCode()); } @Test public void cacheHitOkWithOnlyIfCached() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "only-if-cached"); execute(req1); final ClassicHttpResponse result = execute(req2); assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result)); } @Test public void returns504ForStaleEntryWithOnlyIfCached() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control","max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "only-if-cached"); execute(req1); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, result.getCode()); } @Test public void returnsStaleCacheEntryWithOnlyIfCachedAndMaxStale() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control","max-age=5"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "max-stale=20, only-if-cached"); execute(req1); final ClassicHttpResponse result = execute(req2); assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result)); } @Test public void issues304EvenWithWeakETag() throws Exception { final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "max-age=300"); resp1.setHeader("ETag","W/\"weak-sauce\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("If-None-Match","W/\"weak-sauce\""); execute(req1); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); } } TestProtocolRequirements.java000066400000000000000000007014761434266521000416240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.io.IOException; import java.io.InputStream; import java.net.SocketTimeoutException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.cache.HttpCacheEntry; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.MessageSupport; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** * 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 TestProtocolRequirements { static final int MAX_BYTES = 1024; static final int MAX_ENTRIES = 100; static final int ENTITY_LENGTH = 128; HttpHost host; HttpRoute route; HttpEntity body; HttpClientContext context; @Mock ExecChain mockExecChain; @Mock ExecRuntime mockExecRuntime; @Mock HttpCache mockCache; ClassicHttpRequest request; ClassicHttpResponse originResponse; CacheConfig config; CachingExec impl; HttpCache cache; @BeforeEach public void setUp() throws Exception { MockitoAnnotations.openMocks(this); host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); body = HttpTestUtils.makeBody(ENTITY_LENGTH); request = new BasicClassicHttpRequest("GET", "/foo"); context = HttpClientContext.create(); originResponse = HttpTestUtils.make200Response(); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .build(); cache = new BasicHttpCache(config); impl = new CachingExec(cache, null, config); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); } public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException { return impl.execute( ClassicRequestBuilder.copy(request).build(), new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); } @Test public void testCacheMissOnGETUsesOriginResponse() throws Exception { Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result)); } /* * "Proxy and gateway applications need to be careful when forwarding * messages in protocol versions different from that of the application. * Since the protocol version indicates the protocol capability of the * sender, a proxy/gateway MUST NOT send a message with a version indicator * which is greater than its actual version. If a higher version request is * received, the proxy/gateway MUST either downgrade the request version, or * respond with an error, or switch to tunnel behavior." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.1 */ @Test public void testHigherMajorProtocolVersionsOnRequestSwitchToTunnelBehavior() throws Exception { // tunnel behavior: I don't muck with request or response in // any way request = new BasicClassicHttpRequest("GET", "/foo"); request.setVersion(new ProtocolVersion("HTTP", 2, 13)); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertSame(originResponse, result); } @Test public void testHigher1_XProtocolVersionsDowngradeTo1_1() throws Exception { request = new BasicClassicHttpRequest("GET", "/foo"); request.setVersion(new ProtocolVersion("HTTP", 1, 2)); final ClassicHttpRequest downgraded = new BasicClassicHttpRequest("GET", "/foo"); downgraded.setVersion(HttpVersion.HTTP_1_1); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(downgraded), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result)); } /* * "Due to interoperability problems with HTTP/1.0 proxies discovered since * the publication of RFC 2068[33], caching proxies MUST, gateways MAY, and * tunnels MUST NOT upgrade the request to the highest version they support. * The proxy/gateway's response to that request MUST be in the same major * version as the request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.1 */ @Test public void testRequestsWithLowerProtocolVersionsGetUpgradedTo1_1() throws Exception { request = new BasicClassicHttpRequest("GET", "/foo"); request.setVersion(new ProtocolVersion("HTTP", 1, 0)); final ClassicHttpRequest upgraded = new BasicClassicHttpRequest("GET", "/foo"); upgraded.setVersion(HttpVersion.HTTP_1_1); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(upgraded), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertTrue(HttpTestUtils.semanticallyTransparent(originResponse, result)); } /* * "An HTTP server SHOULD send a response version equal to the highest * version for which the server is at least conditionally compliant, and * whose major version is less than or equal to the one received in the * request." * * http://www.ietf.org/rfc/rfc2145.txt */ @Test public void testLowerOriginResponsesUpgradedToOurVersion1_1() throws Exception { originResponse = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); originResponse.setVersion(new ProtocolVersion("HTTP", 1, 2)); originResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); originResponse.setHeader("Server", "MockOrigin/1.0"); originResponse.setEntity(body); // not testing this internal behavior in this test, just want // to check the protocol version that comes out the other end Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(HttpVersion.HTTP_1_1, result.getVersion()); } @Test public void testResponseToA1_0RequestShouldUse1_1() throws Exception { request = new BasicClassicHttpRequest("GET", "/foo"); request.setVersion(new ProtocolVersion("HTTP", 1, 0)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(HttpVersion.HTTP_1_1, result.getVersion()); } /* * "A proxy MUST forward an unknown header, unless it is protected by a * Connection header." http://www.ietf.org/rfc/rfc2145.txt */ @Test public void testForwardsUnknownHeadersOnRequestsFromHigherProtocolVersions() throws Exception { request = new BasicClassicHttpRequest("GET", "/foo"); request.setVersion(new ProtocolVersion("HTTP", 1, 2)); request.removeHeaders("Connection"); request.addHeader("X-Unknown-Header", "some-value"); final ClassicHttpRequest downgraded = new BasicClassicHttpRequest("GET", "/foo"); downgraded.setVersion(HttpVersion.HTTP_1_1); downgraded.removeHeaders("Connection"); downgraded.addHeader("X-Unknown-Header", "some-value"); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(downgraded), Mockito.any())).thenReturn(originResponse); execute(request); } /* * "A server MUST NOT send transfer-codings to an HTTP/1.0 client." * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6 */ @Test public void testTransferCodingsAreNotSentToAnHTTP_1_0Client() throws Exception { originResponse.setHeader("Transfer-Encoding", "identity"); final ClassicHttpRequest originalRequest = new BasicClassicHttpRequest("GET", "/foo"); originalRequest.setVersion(new ProtocolVersion("HTTP", 1, 0)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(originalRequest); Assertions.assertNull(result.getFirstHeader("TE")); Assertions.assertNull(result.getFirstHeader("Transfer-Encoding")); } /* * "Multiple message-header fields with the same field-name MAY be present * in a message if and only if the entire field-value for that header field * is defined as a comma-separated list [i.e., #(values)]. It MUST be * possible to combine the multiple header fields into one * "field-name: field-value" pair, without changing the semantics of the * message, by appending each subsequent field-value to the first, each * separated by a comma. The order in which header fields with the same * field-name are received is therefore significant to the interpretation of * the combined field value, and thus a proxy MUST NOT change the order of * these field values when a message is forwarded." * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 */ private void testOrderOfMultipleHeadersIsPreservedOnRequests(final String h, final ClassicHttpRequest request) throws Exception { Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest forwarded = reqCapture.getValue(); final String expected = HttpTestUtils.getCanonicalHeaderValue(request, h); final String actual = HttpTestUtils.getCanonicalHeaderValue(forwarded, h); if (!actual.contains(expected)) { Assertions.assertEquals(expected, actual); } } @Test public void testOrderOfMultipleAcceptHeaderValuesIsPreservedOnRequests() throws Exception { request.addHeader("Accept", "audio/*; q=0.2, audio/basic"); request.addHeader("Accept", "text/*, text/html, text/html;level=1, */*"); testOrderOfMultipleHeadersIsPreservedOnRequests("Accept", request); } @Test public void testOrderOfMultipleAcceptCharsetHeadersIsPreservedOnRequests() throws Exception { request.addHeader("Accept-Charset", "iso-8859-5"); request.addHeader("Accept-Charset", "unicode-1-1;q=0.8"); testOrderOfMultipleHeadersIsPreservedOnRequests("Accept-Charset", request); } @Test public void testOrderOfMultipleAcceptEncodingHeadersIsPreservedOnRequests() throws Exception { request.addHeader("Accept-Encoding", "identity"); request.addHeader("Accept-Encoding", "compress, gzip"); testOrderOfMultipleHeadersIsPreservedOnRequests("Accept-Encoding", request); } @Test public void testOrderOfMultipleAcceptLanguageHeadersIsPreservedOnRequests() throws Exception { request.addHeader("Accept-Language", "da, en-gb;q=0.8, en;q=0.7"); request.addHeader("Accept-Language", "i-cherokee"); testOrderOfMultipleHeadersIsPreservedOnRequests("Accept-Encoding", request); } @Test public void testOrderOfMultipleAllowHeadersIsPreservedOnRequests() throws Exception { final BasicClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/"); put.setEntity(body); put.addHeader("Allow", "GET, HEAD"); put.addHeader("Allow", "DELETE"); put.addHeader("Content-Length", "128"); testOrderOfMultipleHeadersIsPreservedOnRequests("Allow", put); } @Test public void testOrderOfMultipleCacheControlHeadersIsPreservedOnRequests() throws Exception { request.addHeader("Cache-Control", "max-age=5"); request.addHeader("Cache-Control", "min-fresh=10"); testOrderOfMultipleHeadersIsPreservedOnRequests("Cache-Control", request); } @Test public void testOrderOfMultipleContentEncodingHeadersIsPreservedOnRequests() throws Exception { final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setEntity(body); post.addHeader("Content-Encoding", "gzip"); post.addHeader("Content-Encoding", "compress"); post.addHeader("Content-Length", "128"); testOrderOfMultipleHeadersIsPreservedOnRequests("Content-Encoding", post); } @Test public void testOrderOfMultipleContentLanguageHeadersIsPreservedOnRequests() throws Exception { final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setEntity(body); post.addHeader("Content-Language", "mi"); post.addHeader("Content-Language", "en"); post.addHeader("Content-Length", "128"); testOrderOfMultipleHeadersIsPreservedOnRequests("Content-Language", post); } @Test public void testOrderOfMultipleExpectHeadersIsPreservedOnRequests() throws Exception { final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setEntity(body); post.addHeader("Expect", "100-continue"); post.addHeader("Expect", "x-expect=true"); post.addHeader("Content-Length", "128"); testOrderOfMultipleHeadersIsPreservedOnRequests("Expect", post); } @Test public void testOrderOfMultiplePragmaHeadersIsPreservedOnRequests() throws Exception { request.addHeader("Pragma", "no-cache"); request.addHeader("Pragma", "x-pragma-1, x-pragma-2"); testOrderOfMultipleHeadersIsPreservedOnRequests("Pragma", request); } @Test public void testOrderOfMultipleViaHeadersIsPreservedOnRequests() throws Exception { request.addHeader("Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)"); request.addHeader("Via", "1.0 ricky, 1.1 mertz, 1.0 lucy"); testOrderOfMultipleHeadersIsPreservedOnRequests("Via", request); } @Test public void testOrderOfMultipleWarningHeadersIsPreservedOnRequests() throws Exception { request.addHeader("Warning", "199 fred \"bargle\""); request.addHeader("Warning", "199 barney \"bungle\""); testOrderOfMultipleHeadersIsPreservedOnRequests("Warning", request); } private void testOrderOfMultipleHeadersIsPreservedOnResponses(final String h) throws Exception { Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertNotNull(result); Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(originResponse, h), HttpTestUtils .getCanonicalHeaderValue(result, h)); } @Test public void testOrderOfMultipleAllowHeadersIsPreservedOnResponses() throws Exception { originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed"); originResponse.addHeader("Allow", "HEAD"); originResponse.addHeader("Allow", "DELETE"); testOrderOfMultipleHeadersIsPreservedOnResponses("Allow"); } @Test public void testOrderOfMultipleCacheControlHeadersIsPreservedOnResponses() throws Exception { originResponse.addHeader("Cache-Control", "max-age=0"); originResponse.addHeader("Cache-Control", "no-store, must-revalidate"); testOrderOfMultipleHeadersIsPreservedOnResponses("Cache-Control"); } @Test public void testOrderOfMultipleContentEncodingHeadersIsPreservedOnResponses() throws Exception { originResponse.addHeader("Content-Encoding", "gzip"); originResponse.addHeader("Content-Encoding", "compress"); testOrderOfMultipleHeadersIsPreservedOnResponses("Content-Encoding"); } @Test public void testOrderOfMultipleContentLanguageHeadersIsPreservedOnResponses() throws Exception { originResponse.addHeader("Content-Language", "mi"); originResponse.addHeader("Content-Language", "en"); testOrderOfMultipleHeadersIsPreservedOnResponses("Content-Language"); } @Test public void testOrderOfMultiplePragmaHeadersIsPreservedOnResponses() throws Exception { originResponse.addHeader("Pragma", "no-cache, x-pragma-2"); originResponse.addHeader("Pragma", "x-pragma-1"); testOrderOfMultipleHeadersIsPreservedOnResponses("Pragma"); } @Test public void testOrderOfMultipleViaHeadersIsPreservedOnResponses() throws Exception { originResponse.addHeader("Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)"); originResponse.addHeader("Via", "1.0 ricky, 1.1 mertz, 1.0 lucy"); testOrderOfMultipleHeadersIsPreservedOnResponses("Via"); } @Test public void testOrderOfMultipleWWWAuthenticateHeadersIsPreservedOnResponses() throws Exception { originResponse.addHeader("WWW-Authenticate", "x-challenge-1"); originResponse.addHeader("WWW-Authenticate", "x-challenge-2"); testOrderOfMultipleHeadersIsPreservedOnResponses("WWW-Authenticate"); } /* * "However, applications MUST understand the class of any status code, as * indicated by the first digit, and treat any unrecognized response as * being equivalent to the x00 status code of that class, with the exception * that an unrecognized response MUST NOT be cached." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1 */ private void testUnknownResponseStatusCodeIsNotCached(final int code) throws Exception { originResponse = new BasicClassicHttpResponse(code, "Moo"); originResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); originResponse.setHeader("Server", "MockOrigin/1.0"); originResponse.setHeader("Cache-Control", "max-age=3600"); originResponse.setEntity(body); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); // in particular, there were no storage calls on the cache Mockito.verifyNoInteractions(mockCache); } @Test public void testUnknownResponseStatusCodesAreNotCached() throws Exception { for (int i = 102; i <= 199; i++) { testUnknownResponseStatusCodeIsNotCached(i); } for (int i = 207; i <= 299; i++) { testUnknownResponseStatusCodeIsNotCached(i); } for (int i = 308; i <= 399; i++) { testUnknownResponseStatusCodeIsNotCached(i); } for (int i = 418; i <= 499; i++) { testUnknownResponseStatusCodeIsNotCached(i); } for (int i = 506; i <= 999; i++) { testUnknownResponseStatusCodeIsNotCached(i); } } /* * "Unrecognized header fields SHOULD be ignored by the recipient and MUST * be forwarded by transparent proxies." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.1 */ @Test public void testUnknownHeadersOnRequestsAreForwarded() throws Exception { request.addHeader("X-Unknown-Header", "blahblah"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest forwarded = reqCapture.getValue(); final Header[] hdrs = forwarded.getHeaders("X-Unknown-Header"); Assertions.assertEquals(1, hdrs.length); Assertions.assertEquals("blahblah", hdrs[0].getValue()); } @Test public void testUnknownHeadersOnResponsesAreForwarded() throws Exception { originResponse.addHeader("X-Unknown-Header", "blahblah"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); final Header[] hdrs = result.getHeaders("X-Unknown-Header"); Assertions.assertEquals(1, hdrs.length); Assertions.assertEquals("blahblah", hdrs[0].getValue()); } /* * "If a client will wait for a 100 (Continue) response before sending the * request body, it MUST send an Expect request-header field (section 14.20) * with the '100-continue' expectation." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3 */ @Test public void testRequestsExpecting100ContinueBehaviorShouldSetExpectHeader() throws Exception { final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); post.setHeader("Content-Length", "128"); post.setEntity(new StringEntity("whatever")); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(post); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest forwarded = reqCapture.getValue(); boolean foundExpect = false; final Iterator it = MessageSupport.iterate(forwarded, HttpHeaders.EXPECT); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("100-continue".equalsIgnoreCase(elt.getName())) { foundExpect = true; break; } } Assertions.assertTrue(foundExpect); } /* * "If a client will wait for a 100 (Continue) response before sending the * request body, it MUST send an Expect request-header field (section 14.20) * with the '100-continue' expectation." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3 */ @Test public void testRequestsNotExpecting100ContinueBehaviorShouldNotSetExpectContinueHeader() throws Exception { final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setHeader("Content-Length", "128"); post.setEntity(new StringEntity("whatever")); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(post); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest forwarded = reqCapture.getValue(); boolean foundExpect = false; final Iterator it = MessageSupport.iterate(forwarded, HttpHeaders.EXPECT); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("100-continue".equalsIgnoreCase(elt.getName())) { foundExpect = true; break; } } Assertions.assertFalse(foundExpect); } /* * "If a proxy receives a request that includes an Expect request- header * field with the '100-continue' expectation, and the proxy either knows * that the next-hop server complies with HTTP/1.1 or higher, or does not * know the HTTP version of the next-hop server, it MUST forward the * request, including the Expect header field. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3 */ @Test public void testExpectHeadersAreForwardedOnRequests() throws Exception { // This would mostly apply to us if we were part of an // application that was a proxy, and would be the // responsibility of the greater application. Our // responsibility is to make sure that if we get an // entity-enclosing request that we properly set (or unset) // the Expect header per the request.expectContinue() flag, // which is tested by the previous few tests. } /* * "A proxy MUST NOT forward a 100 (Continue) response if the request * message was received from an HTTP/1.0 (or earlier) client and did not * include an Expect request-header field with the '100-continue' * expectation. This requirement overrides the general rule for forwarding * of 1xx responses (see section 10.1)." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3 */ @Test public void test100ContinueResponsesAreNotForwardedTo1_0ClientsWhoDidNotAskForThem() throws Exception { final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setVersion(new ProtocolVersion("HTTP", 1, 0)); post.setEntity(body); post.setHeader("Content-Length", "128"); originResponse = new BasicClassicHttpResponse(100, "Continue"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); // if a 100 response gets up to us from the HttpClient // backend, we can't really handle it at that point Assertions.assertThrows(ClientProtocolException.class, () -> execute(post)); } /* * "9.2 OPTIONS. ...Responses to this method are not cacheable. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2 */ @Test public void testResponsesToOPTIONSAreNotCacheable() throws Exception { request = new BasicClassicHttpRequest("OPTIONS", "/"); originResponse.addHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } /* * "A 200 response SHOULD .... If no response body is included, the response * MUST include a Content-Length field with a field-value of '0'." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2 */ @Test public void test200ResponseToOPTIONSWithNoBodyShouldIncludeContentLengthZero() throws Exception { request = new BasicClassicHttpRequest("OPTIONS", "/"); originResponse.setEntity(null); originResponse.setHeader("Content-Length", "0"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); final Header contentLength = result.getFirstHeader("Content-Length"); Assertions.assertNotNull(contentLength); Assertions.assertEquals("0", contentLength.getValue()); } /* * "When a proxy receives an OPTIONS request on an absoluteURI for which * request forwarding is permitted, the proxy MUST check for a Max-Forwards * field. If the Max-Forwards field-value is zero ("0"), the proxy MUST NOT * forward the message; instead, the proxy SHOULD respond with its own * communication options." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2 */ @Test public void testDoesNotForwardOPTIONSWhenMaxForwardsIsZeroOnAbsoluteURIRequest() throws Exception { request = new BasicClassicHttpRequest("OPTIONS", "*"); request.setHeader("Max-Forwards", "0"); execute(request); } /* * "If the Max-Forwards field-value is an integer greater than zero, the * proxy MUST decrement the field-value when it forwards the request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2 */ @Test public void testDecrementsMaxForwardsWhenForwardingOPTIONSRequest() throws Exception { request = new BasicClassicHttpRequest("OPTIONS", "*"); request.setHeader("Max-Forwards", "7"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); Assertions.assertEquals("6", captured.getFirstHeader("Max-Forwards").getValue()); } /* * "If no Max-Forwards field is present in the request, then the forwarded * request MUST NOT include a Max-Forwards field." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2 */ @Test public void testDoesNotAddAMaxForwardsHeaderToForwardedOPTIONSRequests() throws Exception { request = new BasicClassicHttpRequest("OPTIONS", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest forwarded = reqCapture.getValue(); Assertions.assertNull(forwarded.getFirstHeader("Max-Forwards")); } /* * "The HEAD method is identical to GET except that the server MUST NOT * return a message-body in the response." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4 */ @Test public void testResponseToAHEADRequestMustNotHaveABody() throws Exception { request = new BasicClassicHttpRequest("HEAD", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertTrue(result.getEntity() == null || result.getEntity().getContentLength() == 0); } /* * "If the new field values indicate that the cached entity differs from the * current entity (as would be indicated by a change in Content-Length, * Content-MD5, ETag or Last-Modified), then the cache MUST treat the cache * entry as stale." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4 */ private void testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale(final String eHeader, final String oldVal, final String newVal) throws Exception { // put something cacheable in the cache final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.addHeader("Cache-Control", "max-age=3600"); resp1.setHeader(eHeader, oldVal); // get a head that penetrates the cache final ClassicHttpRequest req2 = new BasicClassicHttpRequest("HEAD", "/"); req2.addHeader("Cache-Control", "no-cache"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setEntity(null); resp2.setHeader(eHeader, newVal); // next request doesn't tolerate stale entry final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.addHeader("Cache-Control", "max-stale=0"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); resp3.setHeader(eHeader, newVal); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req1), Mockito.any())).thenReturn(originResponse); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req2), Mockito.any())).thenReturn(originResponse); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req3), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testHEADResponseWithUpdatedContentLengthFieldMakeACacheEntryStale() throws Exception { testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("Content-Length", "128", "127"); } @Test public void testHEADResponseWithUpdatedContentMD5FieldMakeACacheEntryStale() throws Exception { testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==", "Q2hlY2sgSW50ZWdyaXR5IR=="); } @Test public void testHEADResponseWithUpdatedETagFieldMakeACacheEntryStale() throws Exception { testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("ETag", "\"etag1\"", "\"etag2\""); } @Test public void testHEADResponseWithUpdatedLastModifiedFieldMakeACacheEntryStale() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant sixSecondsAgo = now.minusSeconds(6); testHEADResponseWithUpdatedEntityFieldsMakeACacheEntryStale("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo), DateUtils.formatStandardDate(sixSecondsAgo)); } /* * "9.5 POST. Responses to this method are not cacheable, unless the * response includes appropriate Cache-Control or Expires header fields." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5 */ @Test public void testResponsesToPOSTWithoutCacheControlOrExpiresAreNotCached() throws Exception { final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setHeader("Content-Length", "128"); post.setEntity(HttpTestUtils.makeBody(128)); originResponse.removeHeaders("Cache-Control"); originResponse.removeHeaders("Expires"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(post); Mockito.verifyNoInteractions(mockCache); } /* * "9.5 PUT. ...Responses to this method are not cacheable." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6 */ @Test public void testResponsesToPUTsAreNotCached() throws Exception { final BasicClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/"); put.setEntity(HttpTestUtils.makeBody(128)); put.addHeader("Content-Length", "128"); originResponse.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(put); Mockito.verifyNoInteractions(mockCache); } /* * "9.6 DELETE. ... Responses to this method are not cacheable." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7 */ @Test public void testResponsesToDELETEsAreNotCached() throws Exception { request = new BasicClassicHttpRequest("DELETE", "/"); originResponse.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } /* * "9.8 TRACE ... Responses to this method MUST NOT be cached." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8 */ @Test public void testResponsesToTRACEsAreNotCached() throws Exception { request = new BasicClassicHttpRequest("TRACE", "/"); originResponse.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } /* * "The [206] response MUST include the following header fields: * * - Either a Content-Range header field (section 14.16) indicating the * range included with this response, or a multipart/byteranges Content-Type * including Content-Range fields for each part. If a Content-Length header * field is present in the response, its value MUST match the actual number * of OCTETs transmitted in the message-body. * * - Date * * - ETag and/or Content-Location, if the header would have been sent in a * 200 response to the same request * * - Expires, Cache-Control, and/or Vary, if the field-value might differ * from that sent in any previous response for the same variant" * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 */ @Test public void test206ResponseGeneratedFromCacheMustHaveContentRangeOrMultipartByteRangesContentType() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Cache-Control", "max-age=3600"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range", "bytes=0-50"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) { if (result.getFirstHeader("Content-Range") == null) { final HeaderElement elt = MessageSupport.parse(result.getFirstHeader("Content-Type"))[0]; Assertions.assertTrue("multipart/byteranges".equalsIgnoreCase(elt.getName())); Assertions.assertNotNull(elt.getParameterByName("boundary")); Assertions.assertNotNull(elt.getParameterByName("boundary").getValue()); Assertions.assertNotEquals("", elt.getParameterByName("boundary").getValue().trim()); } } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } @Test public void test206ResponseGeneratedFromCacheMustHaveABodyThatMatchesContentLengthHeaderIfPresent() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Cache-Control", "max-age=3600"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range", "bytes=0-50"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) { final Header h = result.getFirstHeader("Content-Length"); if (h != null) { final int contentLength = Integer.parseInt(h.getValue()); int bytesRead = 0; final InputStream i = result.getEntity().getContent(); while ((i.read()) != -1) { bytesRead++; } i.close(); Assertions.assertEquals(contentLength, bytesRead); } } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } @Test public void test206ResponseGeneratedFromCacheMustHaveDateHeader() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Cache-Control", "max-age=3600"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range", "bytes=0-50"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); if (HttpStatus.SC_PARTIAL_CONTENT == result.getCode()) { Assertions.assertNotNull(result.getFirstHeader("Date")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } @Test public void test206ResponseReturnedToClientMustHaveDateHeader() throws Exception { request.addHeader("Range", "bytes=0-50"); originResponse = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); originResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); originResponse.setHeader("Server", "MockOrigin/1.0"); originResponse.setEntity(HttpTestUtils.makeBody(500)); originResponse.setHeader("Content-Range", "bytes 0-499/1234"); originResponse.removeHeaders("Date"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertTrue(result.getCode() != HttpStatus.SC_PARTIAL_CONTENT || result.getFirstHeader("Date") != null); } @Test public void test206ContainsETagIfA200ResponseWouldHaveIncludedIt() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); originResponse.addHeader("Cache-Control", "max-age=3600"); originResponse.addHeader("ETag", "\"etag1\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.addHeader("Range", "bytes=0-50"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) { Assertions.assertNotNull(result.getFirstHeader("ETag")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } @Test public void test206ContainsContentLocationIfA200ResponseWouldHaveIncludedIt() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); originResponse.addHeader("Cache-Control", "max-age=3600"); originResponse.addHeader("Content-Location", "http://foo.example.com/other/url"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.addHeader("Range", "bytes=0-50"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) { Assertions.assertNotNull(result.getFirstHeader("Content-Location")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } @Test public void test206ResponseIncludesVariantHeadersIfValueMightDiffer() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.addHeader("Accept-Encoding", "gzip"); final Instant now = Instant.now(); final Instant inOneHour = Instant.now().plus(1, ChronoUnit.HOURS); originResponse.addHeader("Cache-Control", "max-age=3600"); originResponse.addHeader("Expires", DateUtils.formatStandardDate(inOneHour)); originResponse.addHeader("Vary", "Accept-Encoding"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.addHeader("Cache-Control", "no-cache"); req2.addHeader("Accept-Encoding", "gzip"); final Instant nextSecond = Instant.now().plusSeconds(1); final Instant inTwoHoursPlusASec = now.plus(2, ChronoUnit.HOURS).plus(1, ChronoUnit.SECONDS); final ClassicHttpResponse originResponse2 = HttpTestUtils.make200Response(); originResponse2.setHeader("Date", DateUtils.formatStandardDate(nextSecond)); originResponse2.setHeader("Cache-Control", "max-age=7200"); originResponse2.setHeader("Expires", DateUtils.formatStandardDate(inTwoHoursPlusASec)); originResponse2.setHeader("Vary", "Accept-Encoding"); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.addHeader("Range", "bytes=0-50"); req3.addHeader("Accept-Encoding", "gzip"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse2); execute(req2); final ClassicHttpResponse result = execute(req3); if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) { Assertions.assertNotNull(result.getFirstHeader("Expires")); Assertions.assertNotNull(result.getFirstHeader("Cache-Control")); Assertions.assertNotNull(result.getFirstHeader("Vary")); } Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } /* * "If the [206] response is the result of an If-Range request that used a * weak validator, the response MUST NOT include other entity-headers; this * prevents inconsistencies between cached entity-bodies and updated * headers." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 */ @Test public void test206ResponseToConditionalRangeRequestDoesNotIncludeOtherEntityHeaders() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final Instant now = Instant.now(); final Instant oneHourAgo = now.minus(1, ChronoUnit.HOURS); originResponse = HttpTestUtils.make200Response(); originResponse.addHeader("Allow", "GET,HEAD"); originResponse.addHeader("Cache-Control", "max-age=3600"); originResponse.addHeader("Content-Language", "en"); originResponse.addHeader("Content-Encoding", "x-coding"); originResponse.addHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); originResponse.addHeader("Content-Length", "128"); originResponse.addHeader("Content-Type", "application/octet-stream"); originResponse.addHeader("Last-Modified", DateUtils.formatStandardDate(oneHourAgo)); originResponse.addHeader("ETag", "W/\"weak-tag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.addHeader("If-Range", "W/\"weak-tag\""); req2.addHeader("Range", "bytes=0-50"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) { Assertions.assertNull(result.getFirstHeader("Allow")); Assertions.assertNull(result.getFirstHeader("Content-Encoding")); Assertions.assertNull(result.getFirstHeader("Content-Language")); Assertions.assertNull(result.getFirstHeader("Content-MD5")); Assertions.assertNull(result.getFirstHeader("Last-Modified")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } /* * "Otherwise, the [206] response MUST include all of the entity-headers * that would have been returned with a 200 (OK) response to the same * [If-Range] request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 */ @Test public void test206ResponseToIfRangeWithStrongValidatorReturnsAllEntityHeaders() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final Instant now = Instant.now(); final Instant oneHourAgo = now.minus(1, ChronoUnit.HOURS); originResponse.addHeader("Allow", "GET,HEAD"); originResponse.addHeader("Cache-Control", "max-age=3600"); originResponse.addHeader("Content-Language", "en"); originResponse.addHeader("Content-Encoding", "x-coding"); originResponse.addHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); originResponse.addHeader("Content-Length", "128"); originResponse.addHeader("Content-Type", "application/octet-stream"); originResponse.addHeader("Last-Modified", DateUtils.formatStandardDate(oneHourAgo)); originResponse.addHeader("ETag", "\"strong-tag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.addHeader("If-Range", "\"strong-tag\""); req2.addHeader("Range", "bytes=0-50"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_PARTIAL_CONTENT) { Assertions.assertEquals("GET,HEAD", result.getFirstHeader("Allow").getValue()); Assertions.assertEquals("max-age=3600", result.getFirstHeader("Cache-Control").getValue()); Assertions.assertEquals("en", result.getFirstHeader("Content-Language").getValue()); Assertions.assertEquals("x-coding", result.getFirstHeader("Content-Encoding").getValue()); Assertions.assertEquals("Q2hlY2sgSW50ZWdyaXR5IQ==", result.getFirstHeader("Content-MD5") .getValue()); Assertions.assertEquals(originResponse.getFirstHeader("Last-Modified").getValue(), result .getFirstHeader("Last-Modified").getValue()); } Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } /* * "A cache MUST NOT combine a 206 response with other previously cached * content if the ETag or Last-Modified headers do not match exactly, see * 13.5.4." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 */ @Test public void test206ResponseIsNotCombinedWithPreviousContentIfETagDoesNotMatch() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("ETag", "\"etag1\""); final byte[] bytes1 = new byte[128]; Arrays.fill(bytes1, (byte) 1); resp1.setEntity(new ByteArrayEntity(bytes1, null)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "no-cache"); req2.setHeader("Range", "bytes=0-50"); final Instant inOneSecond = now.plusSeconds(1); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setHeader("Date", DateUtils.formatStandardDate(inOneSecond)); resp2.setHeader("Server", resp1.getFirstHeader("Server").getValue()); resp2.setHeader("ETag", "\"etag2\""); resp2.setHeader("Content-Range", "bytes 0-50/128"); final byte[] bytes2 = new byte[51]; Arrays.fill(bytes2, (byte) 2); resp2.setEntity(new ByteArrayEntity(bytes2, null)); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ClassicHttpResponse result = execute(req3); final InputStream i = result.getEntity().getContent(); int b; boolean found1 = false; boolean found2 = false; while ((b = i.read()) != -1) { if (b == 1) { found1 = true; } if (b == 2) { found2 = true; } } i.close(); Assertions.assertFalse(found1 && found2); // mixture of content Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } @Test public void test206ResponseIsNotCombinedWithPreviousContentIfLastModifiedDoesNotMatch() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final Instant oneHourAgo = now.minus(1, ChronoUnit.HOURS); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(oneHourAgo)); final byte[] bytes1 = new byte[128]; Arrays.fill(bytes1, (byte) 1); resp1.setEntity(new ByteArrayEntity(bytes1, null)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "no-cache"); req2.setHeader("Range", "bytes=0-50"); final Instant inOneSecond = now.plusSeconds(1); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setHeader("Date", DateUtils.formatStandardDate(inOneSecond)); resp2.setHeader("Server", resp1.getFirstHeader("Server").getValue()); resp2.setHeader("Last-Modified", DateUtils.formatStandardDate(now)); resp2.setHeader("Content-Range", "bytes 0-50/128"); final byte[] bytes2 = new byte[51]; Arrays.fill(bytes2, (byte) 2); resp2.setEntity(new ByteArrayEntity(bytes2, null)); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ClassicHttpResponse result = execute(req3); final InputStream i = result.getEntity().getContent(); int b; boolean found1 = false; boolean found2 = false; while ((b = i.read()) != -1) { if (b == 1) { found1 = true; } if (b == 2) { found2 = true; } } i.close(); Assertions.assertFalse(found1 && found2); // mixture of content Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } /* * "A cache that does not support the Range and Content-Range headers MUST * NOT cache 206 (Partial) responses." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 */ @Test public void test206ResponsesAreNotCachedIfTheCacheDoesNotSupportRangeAndContentRangeHeaders() throws Exception { if (!impl.supportsRangeAndContentRangeHeaders()) { request = new BasicClassicHttpRequest("GET", "/"); request.addHeader("Range", "bytes=0-50"); originResponse = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT,"Partial Content"); originResponse.setHeader("Content-Range", "bytes 0-50/128"); originResponse.setHeader("Cache-Control", "max-age=3600"); final byte[] bytes = new byte[51]; new Random().nextBytes(bytes); originResponse.setEntity(new ByteArrayEntity(bytes, null)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } } /* * "10.3.4 303 See Other ... The 303 response MUST NOT be cached, but the * response to the second (redirected) request might be cacheable." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4 */ @Test public void test303ResponsesAreNotCached() throws Exception { request = new BasicClassicHttpRequest("GET", "/"); originResponse = new BasicClassicHttpResponse(HttpStatus.SC_SEE_OTHER, "See Other"); originResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); originResponse.setHeader("Server", "MockServer/1.0"); originResponse.setHeader("Cache-Control", "max-age=3600"); originResponse.setHeader("Content-Type", "application/x-cachingclient-test"); originResponse.setHeader("Location", "http://foo.example.com/other"); originResponse.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } /* * "The [304] response MUST include the following header fields: - Date, * unless its omission is required by section 14.18.1 [clockless origin * servers]." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 */ @Test public void test304ResponseWithDateHeaderForwardedFromOriginIncludesDateHeader() throws Exception { request.setHeader("If-None-Match", "\"etag\""); originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED,"Not Modified"); originResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); originResponse.setHeader("Server", "MockServer/1.0"); originResponse.setHeader("ETag", "\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertNotNull(result.getFirstHeader("Date")); } @Test public void test304ResponseGeneratedFromCacheIncludesDateHeader() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); originResponse.setHeader("Cache-Control", "max-age=3600"); originResponse.setHeader("ETag", "\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-None-Match", "\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) { Assertions.assertNotNull(result.getFirstHeader("Date")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } /* * "The [304] response MUST include the following header fields: - ETag * and/or Content-Location, if the header would have been sent in a 200 * response to the same request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 */ @Test public void test304ResponseGeneratedFromCacheIncludesEtagIfOriginResponseDid() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); originResponse.setHeader("Cache-Control", "max-age=3600"); originResponse.setHeader("ETag", "\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-None-Match", "\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) { Assertions.assertNotNull(result.getFirstHeader("ETag")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } @Test public void test304ResponseGeneratedFromCacheIncludesContentLocationIfOriginResponseDid() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); originResponse.setHeader("Cache-Control", "max-age=3600"); originResponse.setHeader("Content-Location", "http://foo.example.com/other"); originResponse.setHeader("ETag", "\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-None-Match", "\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) { Assertions.assertNotNull(result.getFirstHeader("Content-Location")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } /* * "The [304] response MUST include the following header fields: ... - * Expires, Cache-Control, and/or Vary, if the field-value might differ from * that sent in any previous response for the same variant * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 */ @Test public void test304ResponseGeneratedFromCacheIncludesExpiresCacheControlAndOrVaryIfResponseMightDiffer() throws Exception { final Instant now = Instant.now(); final Instant inTwoHours = now.plus(2, ChronoUnit.HOURS); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Accept-Encoding", "gzip"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag", "\"v1\""); resp1.setHeader("Cache-Control", "max-age=7200"); resp1.setHeader("Expires", DateUtils.formatStandardDate(inTwoHours)); resp1.setHeader("Vary", "Accept-Encoding"); resp1.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Accept-Encoding", "gzip"); req1.setHeader("Cache-Control", "no-cache"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag", "\"v2\""); resp2.setHeader("Cache-Control", "max-age=3600"); resp2.setHeader("Expires", DateUtils.formatStandardDate(inTwoHours)); resp2.setHeader("Vary", "Accept-Encoding"); resp2.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH)); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("Accept-Encoding", "gzip"); req3.setHeader("If-None-Match", "\"v2\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ClassicHttpResponse result = execute(req3); if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) { Assertions.assertNotNull(result.getFirstHeader("Expires")); Assertions.assertNotNull(result.getFirstHeader("Cache-Control")); Assertions.assertNotNull(result.getFirstHeader("Vary")); } Mockito.verify(mockExecChain, Mockito.times(3)).proceed(Mockito.any(), Mockito.any()); } /* * "Otherwise (i.e., the conditional GET used a weak validator), the * response MUST NOT include other entity-headers; this prevents * inconsistencies between cached entity-bodies and updated headers." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 */ @Test public void test304GeneratedFromCacheOnWeakValidatorDoesNotIncludeOtherEntityHeaders() throws Exception { final Instant now = Instant.now(); final Instant oneHourAgo = now.minus(1, ChronoUnit.HOURS); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag", "W/\"v1\""); resp1.setHeader("Allow", "GET,HEAD"); resp1.setHeader("Content-Encoding", "x-coding"); resp1.setHeader("Content-Language", "en"); resp1.setHeader("Content-Length", "128"); resp1.setHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); resp1.setHeader("Content-Type", "application/octet-stream"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(oneHourAgo)); resp1.setHeader("Cache-Control", "max-age=7200"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-None-Match", "W/\"v1\""); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req1), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); if (result.getCode() == HttpStatus.SC_NOT_MODIFIED) { Assertions.assertNull(result.getFirstHeader("Allow")); Assertions.assertNull(result.getFirstHeader("Content-Encoding")); Assertions.assertNull(result.getFirstHeader("Content-Length")); Assertions.assertNull(result.getFirstHeader("Content-MD5")); Assertions.assertNull(result.getFirstHeader("Content-Type")); Assertions.assertNull(result.getFirstHeader("Last-Modified")); } Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any()); } /* * "If a 304 response indicates an entity not currently cached, then the * cache MUST disregard the response and repeat the request without the * conditional." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 */ @Test public void testNotModifiedOfNonCachedEntityShouldRevalidateWithUnconditionalGET() throws Exception { final Instant now = Instant.now(); // load cache with cacheable entry final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag", "\"etag1\""); resp1.setHeader("Cache-Control", "max-age=3600"); // force a revalidation final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "max-age=0,max-stale=0"); // unconditional validation doesn't use If-None-Match final ClassicHttpRequest unconditionalValidation = new BasicClassicHttpRequest("GET", "/"); // new response to unconditional validation provides new body final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp1.setHeader("ETag", "\"etag2\""); resp1.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); // this next one will happen once if the cache tries to // conditionally validate, zero if it goes full revalidation Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(unconditionalValidation), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } /* * "If a cache uses a received 304 response to processChallenge a cache entry, the * cache MUST processChallenge the entry to reflect any new field values given in the * response. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 */ @Test public void testCacheEntryIsUpdatedWithNewFieldValuesIn304Response() throws Exception { final Instant now = Instant.now(); final Instant inFiveSeconds = now.plusSeconds(5); final ClassicHttpRequest initialRequest = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse cachedResponse = HttpTestUtils.make200Response(); cachedResponse.setHeader("Cache-Control", "max-age=3600"); cachedResponse.setHeader("ETag", "\"etag\""); final ClassicHttpRequest secondRequest = new BasicClassicHttpRequest("GET", "/"); secondRequest.setHeader("Cache-Control", "max-age=0,max-stale=0"); final ClassicHttpRequest conditionalValidationRequest = new BasicClassicHttpRequest("GET", "/"); conditionalValidationRequest.setHeader("If-None-Match", "\"etag\""); // to be used if the cache generates a conditional validation final ClassicHttpResponse conditionalResponse = HttpTestUtils.make304Response(); conditionalResponse.setHeader("Date", DateUtils.formatStandardDate(inFiveSeconds)); conditionalResponse.setHeader("Server", "MockUtils/1.0"); conditionalResponse.setHeader("ETag", "\"etag\""); conditionalResponse.setHeader("X-Extra", "junk"); // to be used if the cache generates an unconditional validation final ClassicHttpResponse unconditionalResponse = HttpTestUtils.make200Response(); unconditionalResponse.setHeader("Date", DateUtils.formatStandardDate(inFiveSeconds)); unconditionalResponse.setHeader("ETag", "\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(cachedResponse); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(conditionalValidationRequest), Mockito.any())).thenReturn(conditionalResponse); execute(initialRequest); final ClassicHttpResponse result = execute(secondRequest); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); Assertions.assertEquals(DateUtils.formatStandardDate(inFiveSeconds), result.getFirstHeader("Date").getValue()); Assertions.assertEquals("junk", result.getFirstHeader("X-Extra").getValue()); } /* * "10.4.2 401 Unauthorized ... The response MUST include a WWW-Authenticate * header field (section 14.47) containing a challenge applicable to the * requested resource." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 */ @Test public void testMustIncludeWWWAuthenticateHeaderOnAnOrigin401Response() throws Exception { originResponse = new BasicClassicHttpResponse(401, "Unauthorized"); originResponse.setHeader("WWW-Authenticate", "x-scheme x-param"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(401, result.getCode()); Assertions.assertNotNull(result.getFirstHeader("WWW-Authenticate")); } /* * "10.4.6 405 Method Not Allowed ... The response MUST include an Allow * header containing a list of valid methods for the requested resource. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 */ @Test public void testMustIncludeAllowHeaderFromAnOrigin405Response() throws Exception { originResponse = new BasicClassicHttpResponse(405, "Method Not Allowed"); originResponse.setHeader("Allow", "GET, HEAD"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(405, result.getCode()); Assertions.assertNotNull(result.getFirstHeader("Allow")); } /* * "10.4.8 407 Proxy Authentication Required ... The proxy MUST return a * Proxy-Authenticate header field (section 14.33) containing a challenge * applicable to the proxy for the requested resource." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.8 */ @Test public void testMustIncludeProxyAuthenticateHeaderFromAnOrigin407Response() throws Exception { originResponse = new BasicClassicHttpResponse(407, "Proxy Authentication Required"); originResponse.setHeader("Proxy-Authenticate", "x-scheme x-param"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(407, result.getCode()); Assertions.assertNotNull(result.getFirstHeader("Proxy-Authenticate")); } /* * "10.4.17 416 Requested Range Not Satisfiable ... This response MUST NOT * use the multipart/byteranges content-type." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.17 */ @Test public void testMustNotAddMultipartByteRangeContentTypeTo416Response() throws Exception { originResponse = new BasicClassicHttpResponse(416, "Requested Range Not Satisfiable"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(416, result.getCode()); final Iterator it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE); while (it.hasNext()) { final HeaderElement elt = it.next(); Assertions.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName())); } } @Test public void testMustNotUseMultipartByteRangeContentTypeOnCacheGenerated416Responses() throws Exception { originResponse.setEntity(HttpTestUtils.makeBody(ENTITY_LENGTH)); originResponse.setHeader("Content-Length", "128"); originResponse.setHeader("Cache-Control", "max-age=3600"); final ClassicHttpRequest rangeReq = new BasicClassicHttpRequest("GET", "/"); rangeReq.setHeader("Range", "bytes=1000-1200"); final ClassicHttpResponse orig416 = new BasicClassicHttpResponse(416, "Requested Range Not Satisfiable"); // cache may 416 me right away if it understands byte ranges, // ok to delegate to origin though Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(rangeReq), Mockito.any())).thenReturn(orig416); execute(request); final ClassicHttpResponse result = execute(rangeReq); // might have gotten a 416 from the origin or the cache Assertions.assertEquals(416, result.getCode()); final Iterator it = MessageSupport.iterate(result, HttpHeaders.CONTENT_TYPE); while (it.hasNext()) { final HeaderElement elt = it.next(); Assertions.assertFalse("multipart/byteranges".equalsIgnoreCase(elt.getName())); } Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } /* * "A correct cache MUST respond to a request with the most up-to-date * response held by the cache that is appropriate to the request (see * sections 13.2.5, 13.2.6, and 13.12) which meets one of the following * conditions: * * 1. It has been checked for equivalence with what the origin server would * have returned by revalidating the response with the origin server * (section 13.3); * * 2. It is "fresh enough" (see section 13.2). In the default case, this * means it meets the least restrictive freshness requirement of the client, * origin server, and cache (see section 14.9); if the origin server so * specifies, it is the freshness requirement of the origin server alone. * * If a stored response is not "fresh enough" by the most restrictive * freshness requirement of both the client and the origin server, in * carefully considered circumstances the cache MAY still return the * response with the appropriate Warning header (see section 13.1.5 and * 14.46), unless such a response is prohibited (e.g., by a "no-store" * cache-directive, or by a "no-cache" cache-request-directive; see section * 14.9). * * 3. It is an appropriate 304 (Not Modified), 305 (Proxy Redirect), or * error (4xx or 5xx) response message." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.1.1 */ @Test public void testMustReturnACacheEntryIfItCanRevalidateIt() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant nineSecondsAgo = now.minusSeconds(9); final Instant eightSecondsAgo = now.minusSeconds(8); final Header[] hdrs = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(nineSecondsAgo)), new BasicHeader("Cache-Control", "max-age=0"), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Content-Length", "128") }; final byte[] bytes = new byte[128]; new Random().nextBytes(bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); impl = new CachingExec(mockCache, null, config); request = new BasicClassicHttpRequest("GET", "/thing"); final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/thing"); validate.setHeader("If-None-Match", "\"etag\""); final ClassicHttpResponse notModified = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); notModified.setHeader("Date", DateUtils.formatStandardDate(now)); notModified.setHeader("ETag", "\"etag\""); Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(validate), Mockito.any())).thenReturn(notModified); Mockito.when(mockCache.updateCacheEntry( Mockito.eq(host), RequestEquivalent.eq(request), Mockito.eq(entry), ResponseEquivalent.eq(notModified), Mockito.any(), Mockito.any())) .thenReturn(HttpTestUtils.makeCacheEntry()); execute(request); Mockito.verify(mockCache).updateCacheEntry( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testMustReturnAFreshEnoughCacheEntryIfItHasIt() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant nineSecondsAgo = now.plusSeconds(9); final Instant eightSecondsAgo = now.plusSeconds(8); final Header[] hdrs = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(nineSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length", "128") }; final byte[] bytes = new byte[128]; new Random().nextBytes(bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); impl = new CachingExec(mockCache, null, config); request = new BasicClassicHttpRequest("GET", "/thing"); Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(200, result.getCode()); } /* * "If the cache can not communicate with the origin server, then a correct * cache SHOULD respond as above if the response can be correctly served * from the cache; if not it MUST return an error or warning indicating that * there was a communication failure." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.1.1 * * "111 Revalidation failed MUST be included if a cache returns a stale * response because an attempt to revalidate the response failed, due to an * inability to reach the server." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46 */ @Test public void testMustServeAppropriateErrorOrWarningIfNoOriginCommunicationPossible() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant nineSecondsAgo = now.plusSeconds(9); final Instant eightSecondsAgo = now.plusSeconds(8); final Header[] hdrs = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(nineSecondsAgo)), new BasicHeader("Cache-Control", "max-age=0"), new BasicHeader("Content-Length", "128"), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)) }; final byte[] bytes = new byte[128]; new Random().nextBytes(bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); impl = new CachingExec(mockCache, null, config); request = new BasicClassicHttpRequest("GET", "/thing"); Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow( new IOException("can't talk to origin!")); final ClassicHttpResponse result = execute(request); final int status = result.getCode(); Assertions.assertEquals(200, result.getCode()); boolean foundWarning = false; for (final Header h : result.getHeaders("Warning")) { if (h.getValue().split(" ")[0].equals("111")) { foundWarning = true; } } Assertions.assertTrue(foundWarning); } /* * "Whenever a cache returns a response that is neither first-hand nor * "fresh enough" (in the sense of condition 2 in section 13.1.1), it MUST * attach a warning to that effect, using a Warning general-header." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.1.2 */ @Test public void testAttachesWarningHeaderWhenGeneratingStaleResponse() throws Exception { // covered by previous test } /* * "1xx Warnings that describe the freshness or revalidation status of the * response, and so MUST be deleted after a successful revalidation." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.1.2 */ @Test public void test1xxWarningsAreDeletedAfterSuccessfulRevalidation() throws Exception { final Instant now = Instant.now(); final Instant twentyFiveSecondsAgo = now.minusSeconds(25); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(twentyFiveSecondsAgo)); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Cache-Control", "max-age=5"); resp1.setHeader("Warning", "110 squid \"stale stuff\""); resp1.setHeader("Via", "1.1 fred"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/"); validate.setHeader("If-None-Match", "\"etag\""); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Server", "MockServer/1.0"); resp2.setHeader("ETag", "\"etag\""); resp2.setHeader("Via", "1.1 fred"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(validate), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse stale = execute(req1); Assertions.assertNotNull(stale.getFirstHeader("Warning")); final ClassicHttpResponse result1 = execute(req2); final ClassicHttpResponse result2 = execute(req3); boolean found1xxWarning = false; final Iterator it = MessageSupport.iterate(result1, HttpHeaders.WARNING); while (it.hasNext()) { final HeaderElement elt = it.next(); if (elt.getName().startsWith("1")) { found1xxWarning = true; } } final Iterator it2 = MessageSupport.iterate(result2, HttpHeaders.WARNING); while (it2.hasNext()) { final HeaderElement elt = it2.next(); if (elt.getName().startsWith("1")) { found1xxWarning = true; } } Assertions.assertFalse(found1xxWarning); } /* * "2xx Warnings that describe some aspect of the entity body or entity * headers that is not rectified by a revalidation (for example, a lossy * compression of the entity bodies) and which MUST NOT be deleted after a * successful revalidation." * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.1.2 */ @Test public void test2xxWarningsAreNotDeletedAfterSuccessfulRevalidation() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Cache-Control", "max-age=5"); resp1.setHeader("Via", "1.1 xproxy"); resp1.setHeader("Warning", "214 xproxy \"transformed stuff\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest validate = new BasicClassicHttpRequest("GET", "/"); validate.setHeader("If-None-Match", "\"etag\""); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); resp2.setHeader("Server", "MockServer/1.0"); resp2.setHeader("ETag", "\"etag\""); resp1.setHeader("Via", "1.1 xproxy"); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(validate), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse stale = execute(req1); Assertions.assertNotNull(stale.getFirstHeader("Warning")); final ClassicHttpResponse result1 = execute(req2); final ClassicHttpResponse result2 = execute(req3); boolean found214Warning = false; final Iterator it = MessageSupport.iterate(result1, HttpHeaders.WARNING); while (it.hasNext()) { final HeaderElement elt = it.next(); final String[] parts = elt.getName().split(" "); if ("214".equals(parts[0])) { found214Warning = true; } } Assertions.assertTrue(found214Warning); found214Warning = false; final Iterator it2 = MessageSupport.iterate(result2, HttpHeaders.WARNING); while (it2.hasNext()) { final HeaderElement elt = it2.next(); final String[] parts = elt.getName().split(" "); if ("214".equals(parts[0])) { found214Warning = true; } } Assertions.assertTrue(found214Warning); } /* * "When a response is generated from a cache entry, the cache MUST include * a single Age header field in the response with a value equal to the cache * entry's current_age." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.3 */ @Test public void testAgeHeaderPopulatedFromCacheEntryCurrentAge() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant nineSecondsAgo = now.minusSeconds(9); final Instant eightSecondsAgo = now.minusSeconds(8); final Header[] hdrs = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(nineSecondsAgo)), new BasicHeader("Cache-Control", "max-age=3600"), new BasicHeader("Content-Length", "128") }; final byte[] bytes = new byte[128]; new Random().nextBytes(bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, hdrs, bytes); impl = new CachingExec(mockCache, null, config); request = new BasicClassicHttpRequest("GET", "/thing"); Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(200, result.getCode()); Assertions.assertEquals("11", result.getFirstHeader("Age").getValue()); } /* * "If none of Expires, Cache-Control: max-age, or Cache-Control: s-maxage * (see section 14.9.3) appears in the response, and the response does not * include other restrictions on caching, the cache MAY compute a freshness * lifetime using a heuristic. The cache MUST attach Warning 113 to any * response whose age is more than 24 hours if such warning has not already * been added." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.4 * * "113 Heuristic expiration MUST be included if the cache heuristically * chose a freshness lifetime greater than 24 hours and the response's age * is greater than 24 hours." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46 */ @Test public void testHeuristicCacheOlderThan24HoursHasWarningAttached() throws Exception { final Instant now = Instant.now(); final Instant thirtySixHoursAgo = now.minus(26, ChronoUnit.HOURS); final Instant oneYearAgo = now.minus(1, ChronoUnit.HOURS); final Instant requestTime = thirtySixHoursAgo.minusSeconds(1); final Instant responseTime = thirtySixHoursAgo.plusSeconds(1); final Header[] hdrs = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(thirtySixHoursAgo)), new BasicHeader("Cache-Control", "public"), new BasicHeader("Last-Modified", DateUtils.formatStandardDate(oneYearAgo)), new BasicHeader("Content-Length", "128") }; final byte[] bytes = new byte[128]; new Random().nextBytes(bytes); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(requestTime, responseTime, hdrs, bytes); impl = new CachingExec(mockCache, null, config); request = new BasicClassicHttpRequest("GET", "/thing"); final ClassicHttpResponse validated = HttpTestUtils.make200Response(); validated.setHeader("Cache-Control", "public"); validated.setHeader("Last-Modified", DateUtils.formatStandardDate(oneYearAgo)); validated.setHeader("Content-Length", "128"); validated.setEntity(new ByteArrayEntity(bytes, null)); final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(); Mockito.when(mockCache.getCacheEntry(Mockito.eq(host), RequestEquivalent.eq(request))).thenReturn(entry); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(validated); Mockito.when(mockCache.createCacheEntry( Mockito.any(), Mockito.any(), ResponseEquivalent.eq(validated), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(cacheEntry); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(200, result.getCode()); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); if (allRequests.isEmpty()) { // heuristic cache hit boolean found113Warning = false; final Iterator it = MessageSupport.iterate(result, HttpHeaders.WARNING); while (it.hasNext()) { final HeaderElement elt = it.next(); final String[] parts = elt.getName().split(" "); if ("113".equals(parts[0])) { found113Warning = true; break; } } Assertions.assertTrue(found113Warning); } Mockito.verify(mockCache).createCacheEntry( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } /* * "If a cache has two fresh responses for the same representation with * different validators, it MUST use the one with the more recent Date * header." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.5 */ @Test public void testKeepsMostRecentDateHeaderForFreshResponse() throws Exception { final Instant now = Instant.now(); final Instant inFiveSecond = now.plusSeconds(5); // put an entry in the cache final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(inFiveSecond)); resp1.setHeader("ETag", "\"etag1\""); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Content-Length", "128"); // force another origin hit final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "no-cache"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); // older resp2.setHeader("ETag", "\"etag2\""); resp2.setHeader("Cache-Control", "max-age=3600"); resp2.setHeader("Content-Length", "128"); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ClassicHttpResponse result = execute(req3); Assertions.assertEquals("\"etag1\"", result.getFirstHeader("ETag").getValue()); } /* * "Clients MAY issue simple (non-subrange) GET requests with either weak * validators or strong validators. Clients MUST NOT use weak validators in * other forms of request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3 * * Note that we can't determine a priori whether a given HTTP-date is a weak * or strong validator, because that might depend on an upstream client * having a cache with a Last-Modified and Date entry that allows the date * to be a strong validator. We can tell when *we* are generating a request * for validation, but we can't tell if we receive a conditional request * from upstream. */ private ClassicHttpResponse testRequestWithWeakETagValidatorIsNotAllowed(final String header) throws Exception { final ClassicHttpResponse response = execute(request); // it's probably ok to return a 400 (Bad Request) to this client final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); if (!allRequests.isEmpty()) { final ClassicHttpRequest forwarded = reqCapture.getValue(); if (forwarded != null) { final Header h = forwarded.getFirstHeader(header); if (h != null) { Assertions.assertFalse(h.getValue().startsWith("W/")); } } } return response; } @Test public void testSubrangeGETWithWeakETagIsNotAllowed() throws Exception { request = new BasicClassicHttpRequest("GET", "/"); request.setHeader("Range", "bytes=0-500"); request.setHeader("If-Range", "W/\"etag\""); final ClassicHttpResponse response = testRequestWithWeakETagValidatorIsNotAllowed("If-Range"); Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getCode()); } @Test public void testPUTWithIfMatchWeakETagIsNotAllowed() throws Exception { final ClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/"); put.setEntity(HttpTestUtils.makeBody(128)); put.setHeader("Content-Length", "128"); put.setHeader("If-Match", "W/\"etag\""); request = put; testRequestWithWeakETagValidatorIsNotAllowed("If-Match"); } @Test public void testPUTWithIfNoneMatchWeakETagIsNotAllowed() throws Exception { final ClassicHttpRequest put = new BasicClassicHttpRequest("PUT", "/"); put.setEntity(HttpTestUtils.makeBody(128)); put.setHeader("Content-Length", "128"); put.setHeader("If-None-Match", "W/\"etag\""); request = put; testRequestWithWeakETagValidatorIsNotAllowed("If-None-Match"); } @Test public void testDELETEWithIfMatchWeakETagIsNotAllowed() throws Exception { request = new BasicClassicHttpRequest("DELETE", "/"); request.setHeader("If-Match", "W/\"etag\""); testRequestWithWeakETagValidatorIsNotAllowed("If-Match"); } @Test public void testDELETEWithIfNoneMatchWeakETagIsNotAllowed() throws Exception { request = new BasicClassicHttpRequest("DELETE", "/"); request.setHeader("If-None-Match", "W/\"etag\""); testRequestWithWeakETagValidatorIsNotAllowed("If-None-Match"); } /* * "A cache or origin server receiving a conditional request, other than a * full-body GET request, MUST use the strong comparison function to * evaluate the condition." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3 */ @Test public void testSubrangeGETMustUseStrongComparisonForCachedResponse() throws Exception { final Instant now = Instant.now(); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("ETag", "\"etag\""); // according to weak comparison, this would match. Strong // comparison doesn't, because the cache entry's ETag is not // marked weak. Therefore, the If-Range must fail and we must // either get an error back or the full entity, but we better // not get the conditionally-requested Partial Content (206). final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range", "bytes=0-50"); req2.setHeader("If-Range", "W/\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertNotEquals(HttpStatus.SC_PARTIAL_CONTENT, result.getCode()); Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any()); } /* * "HTTP/1.1 clients: - If an entity tag has been provided by the origin * server, MUST use that entity tag in any cache-conditional request (using * If- Match or If-None-Match)." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 */ @Test public void testValidationMustUseETagIfProvidedByOriginServer() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("ETag", "W/\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "max-age=0,max-stale=0"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); Assertions.assertEquals(2, allRequests.size()); final ClassicHttpRequest validation = allRequests.get(1); boolean isConditional = false; final String[] conditionalHeaders = { "If-Range", "If-Modified-Since", "If-Unmodified-Since", "If-Match", "If-None-Match" }; for (final String ch : conditionalHeaders) { if (validation.getFirstHeader(ch) != null) { isConditional = true; break; } } if (isConditional) { boolean foundETag = false; final Iterator it = MessageSupport.iterate(validation, HttpHeaders.IF_MATCH); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("W/\"etag\"".equals(elt.getName())) { foundETag = true; } } final Iterator it2 = MessageSupport.iterate(validation, HttpHeaders.IF_NONE_MATCH); while (it2.hasNext()) { final HeaderElement elt = it2.next(); if ("W/\"etag\"".equals(elt.getName())) { foundETag = true; } } Assertions.assertTrue(foundETag); } } /* * "An HTTP/1.1 caching proxy, upon receiving a conditional request that * includes both a Last-Modified date and one or more entity tags as cache * validators, MUST NOT return a locally cached response to the client * unless that cached response is consistent with all of the conditional * header fields in the request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 */ @Test public void testConditionalRequestWhereNotAllValidatorsMatchCannotBeServedFromCache() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final Instant twentySecondsAgo = now.plusSeconds(20); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("ETag", "W/\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-None-Match", "W/\"etag\""); req2.setHeader("If-Modified-Since", DateUtils.formatStandardDate(twentySecondsAgo)); // must hit the origin again for the second request Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertNotEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } @Test public void testConditionalRequestWhereAllValidatorsMatchMayBeServedFromCache() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(now)); resp1.setHeader("Cache-Control", "max-age=3600"); resp1.setHeader("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("ETag", "W/\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-None-Match", "W/\"etag\""); req2.setHeader("If-Modified-Since", DateUtils.formatStandardDate(tenSecondsAgo)); // may hit the origin again for the second request Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); execute(req2); Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any()); Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any()); } /* * "However, a cache that does not support the Range and Content-Range * headers MUST NOT cache 206 (Partial Content) responses." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4 */ @Test public void testCacheWithoutSupportForRangeAndContentRangeHeadersDoesNotCacheA206Response() throws Exception { if (!impl.supportsRangeAndContentRangeHeaders()) { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Range", "bytes=0-50"); final ClassicHttpResponse resp = new BasicClassicHttpResponse(206, "Partial Content"); resp.setHeader("Content-Range", "bytes 0-50/128"); resp.setHeader("ETag", "\"etag\""); resp.setHeader("Cache-Control", "max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(),Mockito.any())).thenReturn(resp); execute(req); Mockito.verifyNoInteractions(mockCache); } } /* * "A response received with any other status code (e.g. status codes 302 * and 307) MUST NOT be returned in a reply to a subsequent request unless * there are cache-control directives or another header(s) that explicitly * allow it. For example, these include the following: an Expires header * (section 14.21); a 'max-age', 's-maxage', 'must-revalidate', * 'proxy-revalidate', 'public' or 'private' cache-control directive * (section 14.9)." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4 */ @Test public void test302ResponseWithoutExplicitCacheabilityIsNotReturnedFromCache() throws Exception { originResponse = new BasicClassicHttpResponse(302, "Temporary Redirect"); originResponse.setHeader("Location", "http://foo.example.com/other"); originResponse.removeHeaders("Expires"); originResponse.removeHeaders("Cache-Control"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); execute(request); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } /* * "A transparent proxy MUST NOT modify any of the following fields in a * request or response, and it MUST NOT add any of these fields if not * already present: - Content-Location - Content-MD5 - ETag - Last-Modified */ private void testDoesNotModifyHeaderFromOrigin(final String header, final String value) throws Exception { originResponse = HttpTestUtils.make200Response(); originResponse.setHeader(header, value); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(value, result.getFirstHeader(header).getValue()); } @Test public void testDoesNotModifyContentLocationHeaderFromOrigin() throws Exception { final String url = "http://foo.example.com/other"; testDoesNotModifyHeaderFromOrigin("Content-Location", url); } @Test public void testDoesNotModifyContentMD5HeaderFromOrigin() throws Exception { testDoesNotModifyHeaderFromOrigin("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); } @Test public void testDoesNotModifyEtagHeaderFromOrigin() throws Exception { testDoesNotModifyHeaderFromOrigin("Etag", "\"the-etag\""); } @Test public void testDoesNotModifyLastModifiedHeaderFromOrigin() throws Exception { final String lm = DateUtils.formatStandardDate(Instant.now()); testDoesNotModifyHeaderFromOrigin("Last-Modified", lm); } private void testDoesNotAddHeaderToOriginResponse(final String header) throws Exception { originResponse.removeHeaders(header); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertNull(result.getFirstHeader(header)); } @Test public void testDoesNotAddContentLocationToOriginResponse() throws Exception { testDoesNotAddHeaderToOriginResponse("Content-Location"); } @Test public void testDoesNotAddContentMD5ToOriginResponse() throws Exception { testDoesNotAddHeaderToOriginResponse("Content-MD5"); } @Test public void testDoesNotAddEtagToOriginResponse() throws Exception { testDoesNotAddHeaderToOriginResponse("ETag"); } @Test public void testDoesNotAddLastModifiedToOriginResponse() throws Exception { testDoesNotAddHeaderToOriginResponse("Last-Modified"); } private void testDoesNotModifyHeaderFromOriginOnCacheHit(final String header, final String value) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); originResponse = HttpTestUtils.make200Response(); originResponse.setHeader("Cache-Control", "max-age=3600"); originResponse.setHeader(header, value); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(value, result.getFirstHeader(header).getValue()); } @Test public void testDoesNotModifyContentLocationFromOriginOnCacheHit() throws Exception { final String url = "http://foo.example.com/other"; testDoesNotModifyHeaderFromOriginOnCacheHit("Content-Location", url); } @Test public void testDoesNotModifyContentMD5FromOriginOnCacheHit() throws Exception { testDoesNotModifyHeaderFromOriginOnCacheHit("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); } @Test public void testDoesNotModifyEtagFromOriginOnCacheHit() throws Exception { testDoesNotModifyHeaderFromOriginOnCacheHit("Etag", "\"the-etag\""); } @Test public void testDoesNotModifyLastModifiedFromOriginOnCacheHit() throws Exception { final Instant tenSecondsAgo = Instant.now().minusSeconds(10); testDoesNotModifyHeaderFromOriginOnCacheHit("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); } private void testDoesNotAddHeaderOnCacheHit(final String header) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); originResponse.addHeader("Cache-Control", "max-age=3600"); originResponse.removeHeaders(header); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertNull(result.getFirstHeader(header)); } @Test public void testDoesNotAddContentLocationHeaderOnCacheHit() throws Exception { testDoesNotAddHeaderOnCacheHit("Content-Location"); } @Test public void testDoesNotAddContentMD5HeaderOnCacheHit() throws Exception { testDoesNotAddHeaderOnCacheHit("Content-MD5"); } @Test public void testDoesNotAddETagHeaderOnCacheHit() throws Exception { testDoesNotAddHeaderOnCacheHit("ETag"); } @Test public void testDoesNotAddLastModifiedHeaderOnCacheHit() throws Exception { testDoesNotAddHeaderOnCacheHit("Last-Modified"); } private void testDoesNotModifyHeaderOnRequest(final String header, final String value) throws Exception { final BasicClassicHttpRequest req = new BasicClassicHttpRequest("POST","/"); req.setEntity(HttpTestUtils.makeBody(128)); req.setHeader("Content-Length","128"); req.setHeader(header,value); execute(req); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); Assertions.assertEquals(value, captured.getFirstHeader(header).getValue()); } @Test public void testDoesNotModifyContentLocationHeaderOnRequest() throws Exception { final String url = "http://foo.example.com/other"; testDoesNotModifyHeaderOnRequest("Content-Location",url); } @Test public void testDoesNotModifyContentMD5HeaderOnRequest() throws Exception { testDoesNotModifyHeaderOnRequest("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); } @Test public void testDoesNotModifyETagHeaderOnRequest() throws Exception { testDoesNotModifyHeaderOnRequest("ETag","\"etag\""); } @Test public void testDoesNotModifyLastModifiedHeaderOnRequest() throws Exception { final Instant tenSecondsAgo = Instant.now().minusSeconds(10); testDoesNotModifyHeaderOnRequest("Last-Modified", DateUtils.formatStandardDate(tenSecondsAgo)); } private void testDoesNotAddHeaderToRequestIfNotPresent(final String header) throws Exception { final BasicClassicHttpRequest req = new BasicClassicHttpRequest("POST","/"); req.setEntity(HttpTestUtils.makeBody(128)); req.setHeader("Content-Length","128"); req.removeHeaders(header); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); Assertions.assertNull(captured.getFirstHeader(header)); } @Test public void testDoesNotAddContentLocationToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-Location"); } @Test public void testDoesNotAddContentMD5ToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-MD5"); } @Test public void testDoesNotAddETagToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("ETag"); } @Test public void testDoesNotAddLastModifiedToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Last-Modified"); } /* " A transparent proxy MUST NOT modify any of the following * fields in a response: - Expires * but it MAY add any of these fields if not already present. If * an Expires header is added, it MUST be given a field-value * identical to that of the Date header in that response. */ @Test public void testDoesNotModifyExpiresHeaderFromOrigin() throws Exception { final Instant tenSecondsAgo = Instant.now().minusSeconds(10); testDoesNotModifyHeaderFromOrigin("Expires", DateUtils.formatStandardDate(tenSecondsAgo)); } @Test public void testDoesNotModifyExpiresHeaderFromOriginOnCacheHit() throws Exception { final Instant inTenSeconds = Instant.now().plusSeconds(10); testDoesNotModifyHeaderFromOriginOnCacheHit("Expires", DateUtils.formatStandardDate(inTenSeconds)); } @Test public void testExpiresHeaderMatchesDateIfAddedToOriginResponse() throws Exception { originResponse.removeHeaders("Expires"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); final Header expHdr = result.getFirstHeader("Expires"); if (expHdr != null) { Assertions.assertEquals(result.getFirstHeader("Date").getValue(), expHdr.getValue()); } } @Test public void testExpiresHeaderMatchesDateIfAddedToCacheHit() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); originResponse.setHeader("Cache-Control","max-age=3600"); originResponse.removeHeaders("Expires"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); final Header expHdr = result.getFirstHeader("Expires"); if (expHdr != null) { Assertions.assertEquals(result.getFirstHeader("Date").getValue(), expHdr.getValue()); } } /* "A proxy MUST NOT modify or add any of the following fields in * a message that contains the no-transform cache-control * directive, or in any request: - Content-Encoding - Content-Range * - Content-Type" * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.2 */ private void testDoesNotModifyHeaderFromOriginResponseWithNoTransform(final String header, final String value) throws Exception { originResponse.addHeader("Cache-Control","no-transform"); originResponse.setHeader(header, value); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(value, result.getFirstHeader(header).getValue()); } @Test public void testDoesNotModifyContentEncodingHeaderFromOriginResponseWithNoTransform() throws Exception { testDoesNotModifyHeaderFromOriginResponseWithNoTransform("Content-Encoding","gzip"); } @Test public void testDoesNotModifyContentRangeHeaderFromOriginResponseWithNoTransform() throws Exception { request.setHeader("If-Range","\"etag\""); request.setHeader("Range","bytes=0-49"); originResponse = new BasicClassicHttpResponse(206, "Partial Content"); originResponse.setEntity(HttpTestUtils.makeBody(50)); testDoesNotModifyHeaderFromOriginResponseWithNoTransform("Content-Range","bytes 0-49/128"); } @Test public void testDoesNotModifyContentTypeHeaderFromOriginResponseWithNoTransform() throws Exception { testDoesNotModifyHeaderFromOriginResponseWithNoTransform("Content-Type","text/html;charset=utf-8"); } private void testDoesNotModifyHeaderOnCachedResponseWithNoTransform(final String header, final String value) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); originResponse.addHeader("Cache-Control","max-age=3600, no-transform"); originResponse.setHeader(header, value); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(value, result.getFirstHeader(header).getValue()); } @Test public void testDoesNotModifyContentEncodingHeaderOnCachedResponseWithNoTransform() throws Exception { testDoesNotModifyHeaderOnCachedResponseWithNoTransform("Content-Encoding","gzip"); } @Test public void testDoesNotModifyContentTypeHeaderOnCachedResponseWithNoTransform() throws Exception { testDoesNotModifyHeaderOnCachedResponseWithNoTransform("Content-Type","text/html;charset=utf-8"); } @Test public void testDoesNotModifyContentRangeHeaderOnCachedResponseWithNoTransform() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("If-Range","\"etag\""); req1.setHeader("Range","bytes=0-49"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-Range","\"etag\""); req2.setHeader("Range","bytes=0-49"); originResponse.addHeader("Cache-Control","max-age=3600, no-transform"); originResponse.setHeader("Content-Range", "bytes 0-49/128"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals("bytes 0-49/128", result.getFirstHeader("Content-Range").getValue()); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); } @Test public void testDoesNotAddContentEncodingHeaderToOriginResponseWithNoTransformIfNotPresent() throws Exception { originResponse.addHeader("Cache-Control","no-transform"); testDoesNotAddHeaderToOriginResponse("Content-Encoding"); } @Test public void testDoesNotAddContentRangeHeaderToOriginResponseWithNoTransformIfNotPresent() throws Exception { originResponse.addHeader("Cache-Control","no-transform"); testDoesNotAddHeaderToOriginResponse("Content-Range"); } @Test public void testDoesNotAddContentTypeHeaderToOriginResponseWithNoTransformIfNotPresent() throws Exception { originResponse.addHeader("Cache-Control","no-transform"); testDoesNotAddHeaderToOriginResponse("Content-Type"); } /* no add on cache hit with no-transform */ @Test public void testDoesNotAddContentEncodingHeaderToCachedResponseWithNoTransformIfNotPresent() throws Exception { originResponse.addHeader("Cache-Control","no-transform"); testDoesNotAddHeaderOnCacheHit("Content-Encoding"); } @Test public void testDoesNotAddContentRangeHeaderToCachedResponseWithNoTransformIfNotPresent() throws Exception { originResponse.addHeader("Cache-Control","no-transform"); testDoesNotAddHeaderOnCacheHit("Content-Range"); } @Test public void testDoesNotAddContentTypeHeaderToCachedResponseWithNoTransformIfNotPresent() throws Exception { originResponse.addHeader("Cache-Control","no-transform"); testDoesNotAddHeaderOnCacheHit("Content-Type"); } /* no modify on request */ @Test public void testDoesNotAddContentEncodingToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-Encoding"); } @Test public void testDoesNotAddContentRangeToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-Range"); } @Test public void testDoesNotAddContentTypeToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-Type"); } @Test public void testDoesNotAddContentEncodingHeaderToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-Encoding"); } @Test public void testDoesNotAddContentRangeHeaderToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-Range"); } @Test public void testDoesNotAddContentTypeHeaderToRequestIfNotPresent() throws Exception { testDoesNotAddHeaderToRequestIfNotPresent("Content-Type"); } /* "When a cache makes a validating request to a server, and the * server provides a 304 (Not Modified) response or a 206 (Partial * Content) response, the cache then constructs a response to send * to the requesting client. * * If the status code is 304 (Not Modified), the cache uses the * entity-body stored in the cache entry as the entity-body of * this outgoing response. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.3 */ public void testCachedEntityBodyIsUsedForResponseAfter304Validation() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("ETag","\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control","max-age=0, max-stale=0"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); final InputStream i1 = resp1.getEntity().getContent(); final InputStream i2 = result.getEntity().getContent(); int b1, b2; while((b1 = i1.read()) != -1) { b2 = i2.read(); Assertions.assertEquals(b1, b2); } b2 = i2.read(); Assertions.assertEquals(-1, b2); i1.close(); i2.close(); } /* "The end-to-end headers stored in the cache entry are used for * the constructed response, except that ... * * - any end-to-end headers provided in the 304 or 206 response MUST * replace the corresponding headers from the cache entry. * * Unless the cache decides to remove the cache entry, it MUST * also replace the end-to-end headers stored with the cache entry * with corresponding headers received in the incoming response, * except for Warning headers as described immediately above." */ private void decorateWithEndToEndHeaders(final ClassicHttpResponse r) { r.setHeader("Allow","GET"); r.setHeader("Content-Encoding","gzip"); r.setHeader("Content-Language","en"); r.setHeader("Content-Length", "128"); r.setHeader("Content-Location","http://foo.example.com/other"); r.setHeader("Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ=="); r.setHeader("Content-Type", "text/html;charset=utf-8"); r.setHeader("Expires", DateUtils.formatStandardDate(Instant.now().plusSeconds(10))); r.setHeader("Last-Modified", DateUtils.formatStandardDate(Instant.now().minusSeconds(10))); r.setHeader("Location", "http://foo.example.com/other2"); r.setHeader("Pragma", "x-pragma"); r.setHeader("Retry-After","180"); } @Test public void testResponseIncludesCacheEntryEndToEndHeadersForResponseAfter304Validation() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("ETag","\"etag\""); decorateWithEndToEndHeaders(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "max-age=0, max-stale=0"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp2.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp2.setHeader("Server", "MockServer/1.0"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req2), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); final String[] endToEndHeaders = { "Cache-Control", "ETag", "Allow", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Type", "Expires", "Last-Modified", "Location", "Pragma", "Retry-After" }; for(final String h : endToEndHeaders) { Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp1, h), HttpTestUtils.getCanonicalHeaderValue(result, h)); } } @Test public void testUpdatedEndToEndHeadersFrom304ArePassedOnResponseAndUpdatedInCacheEntry() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("ETag","\"etag\""); decorateWithEndToEndHeaders(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "max-age=0, max-stale=0"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp2.setHeader("Cache-Control", "max-age=1800"); resp2.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp2.setHeader("Server", "MockServer/1.0"); resp2.setHeader("Allow", "GET,HEAD"); resp2.setHeader("Content-Language", "en,en-us"); resp2.setHeader("Content-Location", "http://foo.example.com/new"); resp2.setHeader("Content-Type","text/html"); resp2.setHeader("Expires", DateUtils.formatStandardDate(Instant.now().plusSeconds(5))); resp2.setHeader("Location", "http://foo.example.com/new2"); resp2.setHeader("Pragma","x-new-pragma"); resp2.setHeader("Retry-After","120"); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result1 = execute(req2); final ClassicHttpResponse result2 = execute(req3); final String[] endToEndHeaders = { "Date", "Cache-Control", "Allow", "Content-Language", "Content-Location", "Content-Type", "Expires", "Location", "Pragma", "Retry-After" }; for(final String h : endToEndHeaders) { Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h), HttpTestUtils.getCanonicalHeaderValue(result1, h)); Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h), HttpTestUtils.getCanonicalHeaderValue(result2, h)); } } /* "If a header field-name in the incoming response matches more * than one header in the cache entry, all such old headers MUST * be replaced." */ @Test public void testMultiHeadersAreSuccessfullyReplacedOn304Validation() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.addHeader("Cache-Control","max-age=3600"); resp1.addHeader("Cache-Control","public"); resp1.setHeader("ETag","\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control", "max-age=0, max-stale=0"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp2.setHeader("Cache-Control", "max-age=1800"); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result1 = execute(req2); final ClassicHttpResponse result2 = execute(req3); final String h = "Cache-Control"; Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h), HttpTestUtils.getCanonicalHeaderValue(result1, h)); Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(resp2, h), HttpTestUtils.getCanonicalHeaderValue(result2, h)); } /* "If a cache has a stored non-empty set of subranges for an * entity, and an incoming response transfers another subrange, * the cache MAY combine the new subrange with the existing set if * both the following conditions are met: * * - Both the incoming response and the cache entry have a cache * validator. * * - The two cache validators match using the strong comparison * function (see section 13.3.3). * * If either requirement is not met, the cache MUST use only the * most recent partial response (based on the Date values * transmitted with every response, and using the incoming * response if these values are equal or missing), and MUST * discard the other partial information." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.4 */ @Test public void testCannotCombinePartialResponseIfIncomingResponseDoesNotHaveACacheValidator() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final Instant twoSecondsAgo = Instant.now().plusSeconds(2); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("ETag","\"etag1\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testCannotCombinePartialResponseIfCacheEntryDoesNotHaveACacheValidator() throws Exception { final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final Instant twoSecondsAgo = Instant.now().plusSeconds(2); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("ETag","\"etag1\""); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testCannotCombinePartialResponseIfCacheValidatorsDoNotStronglyMatch() throws Exception { final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final Instant twoSecondsAgo = Instant.now().plusSeconds(2); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("ETag","\"etag1\""); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("ETag","\"etag2\""); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testMustDiscardLeastRecentPartialResponseIfIncomingRequestDoesNotHaveCacheValidator() throws Exception { final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final Instant twoSecondsAgo = Instant.now().plusSeconds(2); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("ETag","\"etag1\""); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); // must make this request; cannot serve from cache Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testMustDiscardLeastRecentPartialResponseIfCachedResponseDoesNotHaveCacheValidator() throws Exception { final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final Instant twoSecondsAgo = Instant.now().plusSeconds(2); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("ETag","\"etag1\""); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); // must make this request; cannot serve from cache Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testMustDiscardLeastRecentPartialResponseIfCacheValidatorsDoNotStronglyMatch() throws Exception { final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final Instant twoSecondsAgo = Instant.now().plusSeconds(2); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("Etag","\"etag1\""); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("ETag","\"etag2\""); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); // must make this request; cannot serve from cache Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testMustDiscardLeastRecentPartialResponseIfCacheValidatorsDoNotStronglyMatchEvenIfResponsesOutOfOrder() throws Exception { final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final Instant twoSecondsAgo = Instant.now().plusSeconds(2); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("Etag","\"etag1\""); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("ETag","\"etag2\""); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(twoSecondsAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); // must make this request; cannot serve from cache Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } @Test public void testMustDiscardCachedPartialResponseIfCacheValidatorsDoNotStronglyMatchAndDateHeadersAreEqual() throws Exception { final Instant now = Instant.now(); final Instant oneSecondAgo = now.minusSeconds(1); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp1 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp1.setEntity(HttpTestUtils.makeBody(50)); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Content-Range","bytes 0-49/128"); resp1.setHeader("Etag","\"etag1\""); resp1.setHeader("Server","MockServer/1.0"); resp1.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Range","bytes=50-127"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); resp2.setEntity(HttpTestUtils.makeBody(78)); resp2.setHeader("Cache-Control","max-age=3600"); resp2.setHeader("Content-Range","bytes 50-127/128"); resp2.setHeader("ETag","\"etag2\""); resp2.setHeader("Server","MockServer/1.0"); resp2.setHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); req3.setHeader("Range","bytes=0-49"); final ClassicHttpResponse resp3 = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK"); resp3.setEntity(HttpTestUtils.makeBody(128)); resp3.setHeader("Server","MockServer/1.0"); resp3.setHeader("Date", DateUtils.formatStandardDate(now)); // must make this request; cannot serve from cache Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(req2); execute(req3); } /* "When the cache receives a subsequent request whose Request-URI * specifies one or more cache entries including a Vary header * field, the cache MUST NOT use such a cache entry to construct a * response to the new request unless all of the selecting * request-headers present in the new request match the * corresponding stored request-headers in the original request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 */ @Test public void testCannotUseVariantCacheEntryIfNotAllSelectingRequestHeadersMatch() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Accept-Encoding","gzip"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag1\""); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Vary","Accept-Encoding"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.removeHeaders("Accept-Encoding"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag","\"etag1\""); resp2.setHeader("Cache-Control","max-age=3600"); // not allowed to have a cache hit; must forward request Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } /* "A Vary header field-value of "*" always fails to match and * subsequent requests on that resource can only be properly * interpreted by the origin server." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 */ @Test public void testCannotServeFromCacheForVaryStar() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag1\""); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Vary","*"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag","\"etag1\""); resp2.setHeader("Cache-Control","max-age=3600"); // not allowed to have a cache hit; must forward request Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } /* " If the selecting request header fields for the cached entry * do not match the selecting request header fields of the new * request, then the cache MUST NOT use a cached entry to satisfy * the request unless it first relays the new request to the * origin server in a conditional request and the server responds * with 304 (Not Modified), including an entity tag or * Content-Location that indicates the entity to be used. * * If an entity tag was assigned to a cached representation, the * forwarded request SHOULD be conditional and include the entity * tags in an If-None-Match header field from all its cache * entries for the resource. This conveys to the server the set of * entities currently held by the cache, so that if any one of * these entities matches the requested entity, the server can use * the ETag header field in its 304 (Not Modified) response to * tell the cache which entry is appropriate. If the entity-tag of * the new response matches that of an existing entry, the new * response SHOULD be used to processChallenge the header fields of the * existing entry, and the result MUST be returned to the client. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 */ @Test public void testNonmatchingVariantCannotBeServedFromCacheUnlessConditionallyValidated() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("User-Agent","MyBrowser/1.0"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag1\""); resp1.setHeader("Cache-Control","max-age=3600"); resp1.setHeader("Vary","User-Agent"); resp1.setHeader("Content-Type","application/octet-stream"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("User-Agent","MyBrowser/1.5"); final ClassicHttpResponse resp200 = HttpTestUtils.make200Response(); resp200.setHeader("ETag","\"etag1\""); resp200.setHeader("Vary","User-Agent"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(req2), Mockito.any())).thenReturn(resp200); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_OK, result.getCode()); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any()); Assertions.assertTrue(HttpTestUtils.semanticallyTransparent(resp200, result)); } /* "Some HTTP methods MUST cause a cache to invalidate an * entity. This is either the entity referred to by the * Request-URI, or by the Location or Content-Location headers (if * present). These methods are: * - PUT * - DELETE * - POST * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9 */ protected void testUnsafeOperationInvalidatesCacheForThatUri( final ClassicHttpRequest unsafeReq) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); resp3.setHeader("Cache-Control","public, max-age=3600"); // this origin request MUST happen due to invalidation Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(unsafeReq); execute(req3); } protected ClassicHttpRequest makeRequestWithBody(final String method, final String requestUri) { final ClassicHttpRequest req = new BasicClassicHttpRequest(method, requestUri); final int nbytes = 128; req.setEntity(HttpTestUtils.makeBody(nbytes)); req.setHeader("Content-Length", Long.toString(nbytes)); return req; } @Test public void testPutToUriInvalidatesCacheForThatUri() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("PUT","/"); testUnsafeOperationInvalidatesCacheForThatUri(req); } @Test public void testDeleteToUriInvalidatesCacheForThatUri() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE","/"); testUnsafeOperationInvalidatesCacheForThatUri(req); } @Test public void testPostToUriInvalidatesCacheForThatUri() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("POST","/"); testUnsafeOperationInvalidatesCacheForThatUri(req); } protected void testUnsafeMethodInvalidatesCacheForHeaderUri( final ClassicHttpRequest unsafeReq) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/content"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","public, max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/content"); final ClassicHttpResponse resp3 = HttpTestUtils.make200Response(); resp3.setHeader("Cache-Control","public, max-age=3600"); // this origin request MUST happen due to invalidation Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp3); execute(req1); execute(unsafeReq); execute(req3); } protected void testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader( final ClassicHttpRequest unsafeReq) throws Exception { unsafeReq.setHeader("Content-Location","http://foo.example.com/content"); testUnsafeMethodInvalidatesCacheForHeaderUri(unsafeReq); } protected void testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader( final ClassicHttpRequest unsafeReq) throws Exception { unsafeReq.setHeader("Content-Location","/content"); testUnsafeMethodInvalidatesCacheForHeaderUri(unsafeReq); } protected void testUnsafeMethodInvalidatesCacheForUriInLocationHeader( final ClassicHttpRequest unsafeReq) throws Exception { unsafeReq.setHeader("Location","http://foo.example.com/content"); testUnsafeMethodInvalidatesCacheForHeaderUri(unsafeReq); } @Test public void testPutInvalidatesCacheForThatUriInContentLocationHeader() throws Exception { final ClassicHttpRequest req2 = makeRequestWithBody("PUT","/"); testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(req2); } @Test public void testPutInvalidatesCacheForThatUriInLocationHeader() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("PUT","/"); testUnsafeMethodInvalidatesCacheForUriInLocationHeader(req); } @Test public void testPutInvalidatesCacheForThatUriInRelativeContentLocationHeader() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("PUT","/"); testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader(req); } @Test public void testDeleteInvalidatesCacheForThatUriInContentLocationHeader() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/"); testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(req); } @Test public void testDeleteInvalidatesCacheForThatUriInRelativeContentLocationHeader() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/"); testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader(req); } @Test public void testDeleteInvalidatesCacheForThatUriInLocationHeader() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/"); testUnsafeMethodInvalidatesCacheForUriInLocationHeader(req); } @Test public void testPostInvalidatesCacheForThatUriInContentLocationHeader() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("POST","/"); testUnsafeMethodInvalidatesCacheForUriInContentLocationHeader(req); } @Test public void testPostInvalidatesCacheForThatUriInLocationHeader() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("POST","/"); testUnsafeMethodInvalidatesCacheForUriInLocationHeader(req); } @Test public void testPostInvalidatesCacheForRelativeUriInContentLocationHeader() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("POST","/"); testUnsafeMethodInvalidatesCacheForRelativeUriInContentLocationHeader(req); } /* "In order to prevent denial of service attacks, an invalidation based on the URI * in a Location or Content-Location header MUST only be performed if the host part * is the same as in the Request-URI." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10 */ protected void testUnsafeMethodDoesNotInvalidateCacheForHeaderUri( final ClassicHttpRequest unsafeReq) throws Exception { final HttpHost otherHost = new HttpHost("bar.example.com", 80); final HttpRoute otherRoute = new HttpRoute(otherHost); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/content"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","public, max-age=3600"); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); final ClassicHttpRequest req3 = new BasicClassicHttpRequest("GET", "/content"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(unsafeReq); execute(req3); } protected void testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts( final ClassicHttpRequest unsafeReq) throws Exception { unsafeReq.setHeader("Content-Location","http://bar.example.com/content"); testUnsafeMethodDoesNotInvalidateCacheForHeaderUri(unsafeReq); } protected void testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts( final ClassicHttpRequest unsafeReq) throws Exception { unsafeReq.setHeader("Location","http://bar.example.com/content"); testUnsafeMethodDoesNotInvalidateCacheForHeaderUri(unsafeReq); } @Test public void testPutDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("PUT","/"); testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(req); } @Test public void testPutDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("PUT","/"); testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(req); } @Test public void testPostDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("POST","/"); testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(req); } @Test public void testPostDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts() throws Exception { final ClassicHttpRequest req = makeRequestWithBody("POST","/"); testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(req); } @Test public void testDeleteDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/"); testUnsafeMethodDoesNotInvalidateCacheForUriInContentLocationHeadersFromOtherHosts(req); } @Test public void testDeleteDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/"); testUnsafeMethodDoesNotInvalidateCacheForUriInLocationHeadersFromOtherHosts(req); } /* "All methods that might be expected to cause modifications to the origin * server's resources MUST be written through to the origin server. This * currently includes all methods except for GET and HEAD. A cache MUST NOT * reply to such a request from a client before having transmitted the * request to the inbound server, and having received a corresponding * response from the inbound server." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.11 */ private void testRequestIsWrittenThroughToOrigin(final ClassicHttpRequest req) throws Exception { final ClassicHttpResponse resp = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); final ClassicHttpRequest wrapper = req; Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(wrapper), Mockito.any())).thenReturn(resp); execute(wrapper); } @Test public void testOPTIONSRequestsAreWrittenThroughToOrigin() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("OPTIONS","*"); testRequestIsWrittenThroughToOrigin(req); } @Test public void testPOSTRequestsAreWrittenThroughToOrigin() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("POST","/"); req.setEntity(HttpTestUtils.makeBody(128)); req.setHeader("Content-Length","128"); testRequestIsWrittenThroughToOrigin(req); } @Test public void testPUTRequestsAreWrittenThroughToOrigin() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("PUT","/"); req.setEntity(HttpTestUtils.makeBody(128)); req.setHeader("Content-Length","128"); testRequestIsWrittenThroughToOrigin(req); } @Test public void testDELETERequestsAreWrittenThroughToOrigin() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("DELETE", "/"); testRequestIsWrittenThroughToOrigin(req); } @Test public void testTRACERequestsAreWrittenThroughToOrigin() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("TRACE","/"); testRequestIsWrittenThroughToOrigin(req); } @Test public void testCONNECTRequestsAreWrittenThroughToOrigin() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("CONNECT","/"); testRequestIsWrittenThroughToOrigin(req); } @Test public void testUnknownMethodRequestsAreWrittenThroughToOrigin() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("UNKNOWN","/"); testRequestIsWrittenThroughToOrigin(req); } /* "If a cache receives a value larger than the largest positive * integer it can represent, or if any of its age calculations * overflows, it MUST transmit an Age header with a value of * 2147483648 (2^31)." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.6 */ @Test public void testTransmitsAgeHeaderIfIncomingAgeHeaderTooBig() throws Exception { final String reallyOldAge = "1" + Long.MAX_VALUE; originResponse.setHeader("Age",reallyOldAge); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals("2147483648", result.getFirstHeader("Age").getValue()); } /* "A proxy MUST NOT modify the Allow header field even if it does not * understand all the methods specified, since the user agent might * have other means of communicating with the origin server. * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 */ @Test public void testDoesNotModifyAllowHeaderWithUnknownMethods() throws Exception { final String allowHeaderValue = "GET, HEAD, FOOBAR"; originResponse.setHeader("Allow",allowHeaderValue); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(originResponse,"Allow"), HttpTestUtils.getCanonicalHeaderValue(result, "Allow")); } /* "When a shared cache (see section 13.7) receives a request * containing an Authorization field, it MUST NOT return the * corresponding response as a reply to any other request, unless one * of the following specific exceptions holds: * * 1. If the response includes the "s-maxage" cache-control * directive, the cache MAY use that response in replying to a * subsequent request. But (if the specified maximum age has * passed) a proxy cache MUST first revalidate it with the origin * server, using the request-headers from the new request to allow * the origin server to authenticate the new request. (This is the * defined behavior for s-maxage.) If the response includes "s- * maxage=0", the proxy MUST always revalidate it before re-using * it. * * 2. If the response includes the "must-revalidate" cache-control * directive, the cache MAY use that response in replying to a * subsequent request. But if the response is stale, all caches * MUST first revalidate it with the origin server, using the * request-headers from the new request to allow the origin server * to authenticate the new request. * * 3. If the response includes the "public" cache-control directive, * it MAY be returned in reply to any subsequent request. */ protected void testSharedCacheRevalidatesAuthorizedResponse( final ClassicHttpResponse authorizedResponse, final int minTimes, final int maxTimes) throws Exception { if (config.isSharedCache()) { final String authorization = StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="; final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Authorization",authorization); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control","max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(authorizedResponse); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); Mockito.verify(mockExecChain, Mockito.atLeast(1 + minTimes)).proceed(Mockito.any(), Mockito.any()); Mockito.verify(mockExecChain, Mockito.atMost(1 + maxTimes)).proceed(Mockito.any(), Mockito.any()); } } @Test public void testSharedCacheMustNotNormallyCacheAuthorizedResponses() throws Exception { final ClassicHttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Cache-Control","max-age=3600"); resp.setHeader("ETag","\"etag\""); testSharedCacheRevalidatesAuthorizedResponse(resp, 1, 1); } @Test public void testSharedCacheMayCacheAuthorizedResponsesWithSMaxAgeHeader() throws Exception { final ClassicHttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Cache-Control","s-maxage=3600"); resp.setHeader("ETag","\"etag\""); testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1); } @Test public void testSharedCacheMustRevalidateAuthorizedResponsesWhenSMaxAgeIsZero() throws Exception { final ClassicHttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Cache-Control","s-maxage=0"); resp.setHeader("ETag","\"etag\""); testSharedCacheRevalidatesAuthorizedResponse(resp, 1, 1); } @Test public void testSharedCacheMayCacheAuthorizedResponsesWithMustRevalidate() throws Exception { final ClassicHttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Cache-Control","must-revalidate"); resp.setHeader("ETag","\"etag\""); testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1); } @Test public void testSharedCacheMayCacheAuthorizedResponsesWithCacheControlPublic() throws Exception { final ClassicHttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Cache-Control","public"); testSharedCacheRevalidatesAuthorizedResponse(resp, 0, 1); } protected void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse( final ClassicHttpResponse authorizedResponse) throws Exception { if (config.isSharedCache()) { final String authorization1 = StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="; final String authorization2 = StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Qy"; final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); req1.setHeader("Authorization",authorization1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Authorization",authorization2); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(authorizedResponse); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); Assertions.assertEquals(2, allRequests.size()); final ClassicHttpRequest captured = allRequests.get(1); Assertions.assertEquals(HttpTestUtils.getCanonicalHeaderValue(req2, "Authorization"), HttpTestUtils.getCanonicalHeaderValue(captured, "Authorization")); } } @Test public void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponsesWithSMaxAge() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date",DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Cache-Control","s-maxage=5"); testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(resp1); } @Test public void testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponsesWithMustRevalidate() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date",DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Cache-Control","maxage=5, must-revalidate"); testSharedCacheMustUseNewRequestHeadersWhenRevalidatingAuthorizedResponse(resp1); } /* "If a cache returns a stale response, either because of a max-stale * directive on a request, or because the cache is configured to * override the expiration time of a response, the cache MUST attach a * Warning header to the stale response, using Warning 110 (Response * is stale). * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 * * "110 Response is stale MUST be included whenever the returned * response is stale." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46 */ @Test public void testWarning110IsAddedToStaleResponses() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control","max-age=5"); resp1.setHeader("Etag","\"etag\""); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control","max-stale=60"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); if (allRequests.isEmpty()) { boolean found110Warning = false; final Iterator it = MessageSupport.iterate(result, HttpHeaders.WARNING); while (it.hasNext()) { final HeaderElement elt = it.next(); final String[] parts = elt.getName().split("\\s"); if ("110".equals(parts[0])) { found110Warning = true; break; } } Assertions.assertTrue(found110Warning); } } /* "Field names MUST NOT be included with the no-cache directive in a * request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4 */ @Test public void testDoesNotTransmitNoCacheDirectivesWithFieldsDownstream() throws Exception { request.setHeader("Cache-Control","no-cache=\"X-Field\""); try { execute(request); } catch (final ClientProtocolException acceptable) { } final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.atMostOnce()).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); if (!allRequests.isEmpty()) { final ClassicHttpRequest captured = reqCapture.getValue(); final Iterator it = MessageSupport.iterate(captured, HttpHeaders.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("no-cache".equals(elt.getName())) { Assertions.assertNull(elt.getValue()); } } } } /* "The request includes a "no-cache" cache-control directive or, for * compatibility with HTTP/1.0 clients, "Pragma: no-cache".... The * server MUST NOT use a cached copy when responding to such a request." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4 */ protected void testCacheIsNotUsedWhenRespondingToRequest(final ClassicHttpRequest req) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Etag","\"etag\""); resp1.setHeader("Cache-Control","max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Etag","\"etag2\""); resp2.setHeader("Cache-Control","max-age=1200"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req); Assertions.assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result)); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); Assertions.assertTrue(HttpTestUtils.equivalent(req, captured)); } @Test public void testCacheIsNotUsedWhenRespondingToRequestWithCacheControlNoCache() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Cache-Control","no-cache"); testCacheIsNotUsedWhenRespondingToRequest(req); } @Test public void testCacheIsNotUsedWhenRespondingToRequestWithPragmaNoCache() throws Exception { final ClassicHttpRequest req = new BasicClassicHttpRequest("GET", "/"); req.setHeader("Pragma","no-cache"); testCacheIsNotUsedWhenRespondingToRequest(req); } /* "When the must-revalidate directive is present in a response received * by a cache, that cache MUST NOT use the entry after it becomes stale * to respond to a subsequent request without first revalidating it with * the origin server. (I.e., the cache MUST do an end-to-end * revalidation every time, if, based solely on the origin server's * Expires or max-age value, the cached response is stale.)" * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4 */ protected void testStaleCacheResponseMustBeRevalidatedWithOrigin( final ClassicHttpResponse staleResponse) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control","max-stale=3600"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag","\"etag2\""); resp2.setHeader("Cache-Control","max-age=5, must-revalidate"); // this request MUST happen Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(staleResponse); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.times(2)).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest reval = reqCapture.getValue(); boolean foundMaxAge0 = false; final Iterator it = MessageSupport.iterate(reval, HttpHeaders.CACHE_CONTROL); while (it.hasNext()) { final HeaderElement elt = it.next(); if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) { foundMaxAge0 = true; } } Assertions.assertTrue(foundMaxAge0); } @Test public void testStaleEntryWithMustRevalidateIsNotUsedWithoutRevalidatingWithOrigin() throws Exception { final ClassicHttpResponse response = HttpTestUtils.make200Response(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); response.setHeader("Date",DateUtils.formatStandardDate(tenSecondsAgo)); response.setHeader("ETag","\"etag1\""); response.setHeader("Cache-Control","max-age=5, must-revalidate"); testStaleCacheResponseMustBeRevalidatedWithOrigin(response); } /* "In all circumstances an HTTP/1.1 cache MUST obey the must-revalidate * directive; in particular, if the cache cannot reach the origin server * for any reason, it MUST generate a 504 (Gateway Timeout) response." */ protected void testGenerates504IfCannotRevalidateStaleResponse( final ClassicHttpResponse staleResponse) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(staleResponse); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenThrow(new SocketTimeoutException()); final ClassicHttpResponse result = execute(req2); Assertions.assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, result.getCode()); } @Test public void testGenerates504IfCannotRevalidateAMustRevalidateEntry() throws Exception { final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control","max-age=5,must-revalidate"); testGenerates504IfCannotRevalidateStaleResponse(resp1); } /* "The proxy-revalidate directive has the same meaning as the must- * revalidate directive, except that it does not apply to non-shared * user agent caches." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4 */ @Test public void testStaleEntryWithProxyRevalidateOnSharedCacheIsNotUsedWithoutRevalidatingWithOrigin() throws Exception { if (config.isSharedCache()) { final ClassicHttpResponse response = HttpTestUtils.make200Response(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); response.setHeader("Date",DateUtils.formatStandardDate(tenSecondsAgo)); response.setHeader("ETag","\"etag1\""); response.setHeader("Cache-Control","max-age=5, proxy-revalidate"); testStaleCacheResponseMustBeRevalidatedWithOrigin(response); } } @Test public void testGenerates504IfSharedCacheCannotRevalidateAProxyRevalidateEntry() throws Exception { if (config.isSharedCache()) { final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control","max-age=5,proxy-revalidate"); testGenerates504IfCannotRevalidateStaleResponse(resp1); } } /* "[The cache control directive] "private" Indicates that all or part of * the response message is intended for a single user and MUST NOT be * cached by a shared cache." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1 */ @Test public void testCacheControlPrivateIsNotCacheableBySharedCache() throws Exception { if (config.isSharedCache()) { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "private,max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); // this backend request MUST happen Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } } @Test public void testCacheControlPrivateOnFieldIsNotReturnedBySharedCache() throws Exception { if (config.isSharedCache()) { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("X-Personal", "stuff"); resp1.setHeader("Cache-Control", "private=\"X-Personal\",s-maxage=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); // this backend request MAY happen Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); final ClassicHttpResponse result = execute(req2); Assertions.assertNull(result.getFirstHeader("X-Personal")); Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any()); Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any()); } } /* "If the no-cache directive does not specify a field-name, then a * cache MUST NOT use the response to satisfy a subsequent request * without successful revalidation with the origin server. This allows * an origin server to prevent caching even by caches that have been * configured to return stale responses to client requests." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1 */ @Test public void testNoCacheCannotSatisfyASubsequentRequestWithoutRevalidation() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Cache-Control","no-cache"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); // this MUST happen Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } @Test public void testNoCacheCannotSatisfyASubsequentRequestWithoutRevalidationEvenWithContraryIndications() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Cache-Control","no-cache,s-maxage=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control","max-stale=7200"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); // this MUST happen Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } /* "If the no-cache directive does specify one or more field-names, then * a cache MAY use the response to satisfy a subsequent request, subject * to any other restrictions on caching. However, the specified * field-name(s) MUST NOT be sent in the response to a subsequent request * without successful revalidation with the origin server." */ @Test public void testNoCacheOnFieldIsNotReturnedWithoutRevalidation() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("X-Stuff","things"); resp1.setHeader("Cache-Control","no-cache=\"X-Stuff\", max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("ETag","\"etag\""); resp2.setHeader("X-Stuff","things"); resp2.setHeader("Cache-Control","no-cache=\"X-Stuff\",max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); final ClassicHttpResponse result = execute(req2); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(reqCapture.capture(), Mockito.any()); final List allRequests = reqCapture.getAllValues(); if (allRequests.isEmpty()) { Assertions.assertNull(result.getFirstHeader("X-Stuff")); } } /* "The purpose of the no-store directive is to prevent the inadvertent * release or retention of sensitive information (for example, on backup * tapes). The no-store directive applies to the entire message, and MAY * be sent either in a response or in a request. If sent in a request, a * cache MUST NOT store any part of either this request or any response * to it. If sent in a response, a cache MUST NOT store any part of * either this response or the request that elicited it. This directive * applies to both non- shared and shared caches. "MUST NOT store" in * this context means that the cache MUST NOT intentionally store the * information in non-volatile storage, and MUST make a best-effort * attempt to remove the information from volatile storage as promptly * as possible after forwarding it." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.2 */ @Test public void testNoStoreOnRequestIsNotStoredInCache() throws Exception { request.setHeader("Cache-Control","no-store"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } @Test public void testNoStoreOnRequestIsNotStoredInCacheEvenIfResponseMarkedCacheable() throws Exception { request.setHeader("Cache-Control","no-store"); originResponse.setHeader("Cache-Control","max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } @Test public void testNoStoreOnResponseIsNotStoredInCache() throws Exception { originResponse.setHeader("Cache-Control","no-store"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } @Test public void testNoStoreOnResponseIsNotStoredInCacheEvenWithContraryIndicators() throws Exception { originResponse.setHeader("Cache-Control","no-store,max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); Mockito.verifyNoInteractions(mockCache); } /* "If multiple encodings have been applied to an entity, the content * codings MUST be listed in the order in which they were applied." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11 */ @Test public void testOrderOfMultipleContentEncodingHeaderValuesIsPreserved() throws Exception { originResponse.addHeader("Content-Encoding","gzip"); originResponse.addHeader("Content-Encoding","deflate"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); int total_encodings = 0; final Iterator it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING); while (it.hasNext()) { final HeaderElement elt = it.next(); switch(total_encodings) { case 0: Assertions.assertEquals("gzip", elt.getName()); break; case 1: Assertions.assertEquals("deflate", elt.getName()); break; default: Assertions.fail("too many encodings"); } total_encodings++; } Assertions.assertEquals(2, total_encodings); } @Test public void testOrderOfMultipleParametersInContentEncodingHeaderIsPreserved() throws Exception { originResponse.addHeader("Content-Encoding","gzip,deflate"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); int total_encodings = 0; final Iterator it = MessageSupport.iterate(result, HttpHeaders.CONTENT_ENCODING); while (it.hasNext()) { final HeaderElement elt = it.next(); switch(total_encodings) { case 0: Assertions.assertEquals("gzip", elt.getName()); break; case 1: Assertions.assertEquals("deflate", elt.getName()); break; default: Assertions.fail("too many encodings"); } total_encodings++; } Assertions.assertEquals(2, total_encodings); } /* "A cache cannot assume that an entity with a Content-Location * different from the URI used to retrieve it can be used to respond * to later requests on that Content-Location URI." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.14 */ @Test public void testCacheDoesNotAssumeContentLocationHeaderIndicatesAnotherCacheableResource() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/foo"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","public,max-age=3600"); resp1.setHeader("Etag","\"etag\""); resp1.setHeader("Content-Location","http://foo.example.com/bar"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/bar"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control","public,max-age=3600"); resp2.setHeader("Etag","\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } /* "A received message that does not have a Date header field MUST be * assigned one by the recipient if the message will be cached by that * recipient or gatewayed via a protocol which requires a Date." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 */ @Test public void testCachedResponsesWithMissingDateHeadersShouldBeAssignedOne() throws Exception { originResponse.removeHeaders("Date"); originResponse.setHeader("Cache-Control","public"); originResponse.setHeader("ETag","\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertNotNull(result.getFirstHeader("Date")); } /* "The Expires entity-header field gives the date/time after which the * response is considered stale.... HTTP/1.1 clients and caches MUST * treat other invalid date formats, especially including the value '0', * as in the past (i.e., 'already expired')." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21 */ private void testInvalidExpiresHeaderIsTreatedAsStale( final String expiresHeader) throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","public"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Expires", expiresHeader); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); // second request to origin MUST happen Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } @Test public void testMalformedExpiresHeaderIsTreatedAsStale() throws Exception { testInvalidExpiresHeaderIsTreatedAsStale("garbage"); } @Test public void testExpiresZeroHeaderIsTreatedAsStale() throws Exception { testInvalidExpiresHeaderIsTreatedAsStale("0"); } /* "To mark a response as 'already expired,' an origin server sends * an Expires date that is equal to the Date header value." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21 */ @Test public void testExpiresHeaderEqualToDateHeaderIsTreatedAsStale() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control","public"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Expires", resp1.getFirstHeader("Date").getValue()); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); // second request to origin MUST happen Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); execute(req2); } /* "If the response is being forwarded through a proxy, the proxy * application MUST NOT modify the Server response-header." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.38 */ @Test public void testDoesNotModifyServerResponseHeader() throws Exception { final String server = "MockServer/1.0"; originResponse.setHeader("Server", server); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(server, result.getFirstHeader("Server").getValue()); } /* "If multiple encodings have been applied to an entity, the transfer- * codings MUST be listed in the order in which they were applied." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.41 */ @Test public void testOrderOfMultipleTransferEncodingHeadersIsPreserved() throws Exception { originResponse.addHeader("Transfer-Encoding","chunked"); originResponse.addHeader("Transfer-Encoding","x-transfer"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); int transfer_encodings = 0; final Iterator it = MessageSupport.iterate(result, HttpHeaders.TRANSFER_ENCODING); while (it.hasNext()) { final HeaderElement elt = it.next(); switch(transfer_encodings) { case 0: Assertions.assertEquals("chunked",elt.getName()); break; case 1: Assertions.assertEquals("x-transfer",elt.getName()); break; default: Assertions.fail("too many transfer encodings"); } transfer_encodings++; } Assertions.assertEquals(2, transfer_encodings); } @Test public void testOrderOfMultipleTransferEncodingsInSingleHeadersIsPreserved() throws Exception { originResponse.addHeader("Transfer-Encoding","chunked, x-transfer"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); int transfer_encodings = 0; final Iterator it = MessageSupport.iterate(result, HttpHeaders.TRANSFER_ENCODING); while (it.hasNext()) { final HeaderElement elt = it.next(); switch(transfer_encodings) { case 0: Assertions.assertEquals("chunked",elt.getName()); break; case 1: Assertions.assertEquals("x-transfer",elt.getName()); break; default: Assertions.fail("too many transfer encodings"); } transfer_encodings++; } Assertions.assertEquals(2, transfer_encodings); } /* "A Vary field value of '*' signals that unspecified parameters * not limited to the request-headers (e.g., the network address * of the client), play a role in the selection of the response * representation. The '*' value MUST NOT be generated by a proxy * server; it may only be generated by an origin server." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44 */ @Test public void testVaryStarIsNotGeneratedByProxy() throws Exception { request.setHeader("User-Agent","my-agent/1.0"); originResponse.setHeader("Cache-Control","public, max-age=3600"); originResponse.setHeader("Vary","User-Agent"); originResponse.setHeader("ETag","\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); final Iterator it = MessageSupport.iterate(result, HttpHeaders.VARY); while (it.hasNext()) { final HeaderElement elt = it.next(); Assertions.assertNotEquals("*", elt.getName()); } } /* "The Via general-header field MUST be used by gateways and proxies * to indicate the intermediate protocols and recipients between the * user agent and the server on requests, and between the origin server * and the client on responses." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.45 */ @Test public void testProperlyFormattedViaHeaderIsAddedToRequests() throws Exception { request.removeHeaders("Via"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); execute(request); final ArgumentCaptor reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); final String via = captured.getFirstHeader("Via").getValue(); assertValidViaHeader(via); } @Test public void testProperlyFormattedViaHeaderIsAddedToResponses() throws Exception { originResponse.removeHeaders("Via"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); assertValidViaHeader(result.getFirstHeader("Via").getValue()); } private void assertValidViaHeader(final String via) { // Via = "Via" ":" 1#( received-protocol received-by [ comment ] ) // received-protocol = [ protocol-name "/" ] protocol-version // protocol-name = token // protocol-version = token // received-by = ( host [ ":" port ] ) | pseudonym // pseudonym = token final String[] parts = via.split("\\s+"); Assertions.assertTrue(parts.length >= 2); // received protocol final String receivedProtocol = parts[0]; final String[] protocolParts = receivedProtocol.split("/"); Assertions.assertTrue(protocolParts.length >= 1); Assertions.assertTrue(protocolParts.length <= 2); final String tokenRegexp = "[^\\p{Cntrl}()<>@,;:\\\\\"/\\[\\]?={} \\t]+"; for(final String protocolPart : protocolParts) { Assertions.assertTrue(Pattern.matches(tokenRegexp, protocolPart)); } // received-by if (!Pattern.matches(tokenRegexp, parts[1])) { // host : port new HttpHost(parts[1]); // TODO - unused - is this a test bug? else use Assertions.assertNotNull } // comment if (parts.length > 2) { final StringBuilder buf = new StringBuilder(parts[2]); for(int i=3; i reqCapture = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(mockExecChain).proceed(reqCapture.capture(), Mockito.any()); final ClassicHttpRequest captured = reqCapture.getValue(); final String via = captured.getFirstHeader("Via").getValue(); final String protocol = via.split("\\s+")[0]; final String[] protoParts = protocol.split("/"); if (protoParts.length > 1) { Assertions.assertTrue("http".equalsIgnoreCase(protoParts[0])); } Assertions.assertEquals("1.0",protoParts[protoParts.length-1]); } @Test public void testViaHeaderOnResponseProperlyRecordsOriginProtocol() throws Exception { originResponse = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content"); originResponse.setVersion(HttpVersion.HTTP_1_0); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); final String via = result.getFirstHeader("Via").getValue(); final String protocol = via.split("\\s+")[0]; final String[] protoParts = protocol.split("/"); Assertions.assertTrue(protoParts.length >= 1); Assertions.assertTrue(protoParts.length <= 2); if (protoParts.length > 1) { Assertions.assertTrue("http".equalsIgnoreCase(protoParts[0])); } Assertions.assertEquals("1.0", protoParts[protoParts.length - 1]); } /* "A cache MUST NOT delete any Warning header that it received with * a message." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46 */ @Test public void testRetainsWarningHeadersReceivedFromUpstream() throws Exception { originResponse.removeHeaders("Warning"); final String warning = "199 fred \"misc\""; originResponse.addHeader("Warning", warning); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); Assertions.assertEquals(warning, result.getFirstHeader("Warning").getValue()); } /* "However, if a cache successfully validates a cache entry, it * SHOULD remove any Warning headers previously attached to that * entry except as specified for specific Warning codes. It MUST * then add any Warning headers received in the validating response." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46 */ @Test public void testUpdatesWarningHeadersOnValidation() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final Instant now = Instant.now(); final Instant twentySecondsAgo = now.plusSeconds(20); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(twentySecondsAgo)); resp1.setHeader("Cache-Control","public,max-age=5"); resp1.setHeader("ETag", "\"etag1\""); final String oldWarning = "113 wilma \"stale\""; resp1.setHeader("Warning", oldWarning); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpResponse resp2 = new BasicClassicHttpResponse(HttpStatus.SC_NOT_MODIFIED, "Not Modified"); resp2.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp2.setHeader("ETag", "\"etag1\""); final String newWarning = "113 betty \"stale too\""; resp2.setHeader("Warning", newWarning); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req1); final ClassicHttpResponse result = execute(req2); boolean oldWarningFound = false; boolean newWarningFound = false; for(final Header h : result.getHeaders("Warning")) { for(final String warnValue : h.getValue().split("\\s*,\\s*")) { if (oldWarning.equals(warnValue)) { oldWarningFound = true; } else if (newWarning.equals(warnValue)) { newWarningFound = true; } } } Assertions.assertFalse(oldWarningFound); Assertions.assertTrue(newWarningFound); } /* "If an implementation sends a message with one or more Warning * headers whose version is HTTP/1.0 or lower, then the sender MUST * include in each warning-value a warn-date that matches the date * in the response." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46 */ @Test public void testWarnDatesAreAddedToWarningsOnLowerProtocolVersions() throws Exception { final String dateHdr = DateUtils.formatStandardDate(Instant.now()); final String origWarning = "110 fred \"stale\""; originResponse.setCode(HttpStatus.SC_OK); originResponse.setVersion(HttpVersion.HTTP_1_0); originResponse.addHeader("Warning", origWarning); originResponse.setHeader("Date", dateHdr); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); // note that currently the implementation acts as an HTTP/1.1 proxy, // which means that all the responses from the caching module should // be HTTP/1.1, so we won't actually be testing anything here until // that changes. if (HttpVersion.HTTP_1_0.greaterEquals(result.getVersion())) { Assertions.assertEquals(dateHdr, result.getFirstHeader("Date").getValue()); boolean warningFound = false; final String targetWarning = origWarning + " \"" + dateHdr + "\""; for(final Header h : result.getHeaders("Warning")) { for(final String warning : h.getValue().split("\\s*,\\s*")) { if (targetWarning.equals(warning)) { warningFound = true; break; } } } Assertions.assertTrue(warningFound); } } /* "If an implementation receives a message with a warning-value that * includes a warn-date, and that warn-date is different from the Date * value in the response, then that warning-value MUST be deleted from * the message before storing, forwarding, or using it. (This prevents * bad consequences of naive caching of Warning header fields.) If all * of the warning-values are deleted for this reason, the Warning * header MUST be deleted as well." * * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.46 */ @Test public void testStripsBadlyDatedWarningsFromForwardedResponses() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); originResponse.setHeader("Date", DateUtils.formatStandardDate(now)); originResponse.addHeader("Warning", "110 fred \"stale\", 110 wilma \"stale\" \"" + DateUtils.formatStandardDate(tenSecondsAgo) + "\""); originResponse.setHeader("Cache-Control","no-cache,no-store"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); for(final Header h : result.getHeaders("Warning")) { Assertions.assertFalse(h.getValue().contains("wilma")); } } @Test public void testStripsBadlyDatedWarningsFromStoredResponses() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); originResponse.setHeader("Date", DateUtils.formatStandardDate(now)); originResponse.addHeader("Warning", "110 fred \"stale\", 110 wilma \"stale\" \"" + DateUtils.formatStandardDate(tenSecondsAgo) + "\""); originResponse.setHeader("Cache-Control","public,max-age=3600"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); for(final Header h : result.getHeaders("Warning")) { Assertions.assertFalse(h.getValue().contains("wilma")); } } @Test public void testRemovesWarningHeaderIfAllWarnValuesAreBadlyDated() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); originResponse.setHeader("Date", DateUtils.formatStandardDate(now)); originResponse.addHeader("Warning", "110 wilma \"stale\" \"" + DateUtils.formatStandardDate(tenSecondsAgo) + "\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); final ClassicHttpResponse result = execute(request); final Header[] warningHeaders = result.getHeaders("Warning"); Assertions.assertTrue(warningHeaders == null || warningHeaders.length == 0); } } TestRFC5861Compliance.java000066400000000000000000000647041434266521000403440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.IOException; import java.time.Instant; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.impl.schedule.ImmediateSchedulingStrategy; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** * A suite of acceptance tests for compliance with RFC5861, which * describes the stale-if-error and stale-while-revalidate * Cache-Control extensions. */ public class TestRFC5861Compliance { static final int MAX_BYTES = 1024; static final int MAX_ENTRIES = 100; static final int ENTITY_LENGTH = 128; HttpHost host; HttpRoute route; HttpEntity body; HttpClientContext context; @Mock ExecChain mockExecChain; @Mock ExecRuntime mockExecRuntime; ClassicHttpRequest request; ClassicHttpResponse originResponse; CacheConfig config; CachingExec impl; HttpCache cache; ScheduledExecutorService executorService; @BeforeEach public void setUp() throws Exception { MockitoAnnotations.openMocks(this); host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); body = HttpTestUtils.makeBody(ENTITY_LENGTH); request = new BasicClassicHttpRequest("GET", "/foo"); context = HttpClientContext.create(); originResponse = HttpTestUtils.make200Response(); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .build(); cache = new BasicHttpCache(config); impl = new CachingExec(cache, null, config); executorService = new ScheduledThreadPoolExecutor(1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(originResponse); Mockito.when(mockExecRuntime.fork(null)).thenReturn(mockExecRuntime); } @AfterEach public void cleanup() { executorService.shutdownNow(); } public ClassicHttpResponse execute(final ClassicHttpRequest request) throws IOException, HttpException { return impl.execute( ClassicRequestBuilder.copy(request).build(), new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain); } /* * "The stale-if-error Cache-Control extension indicates that when an * error is encountered, a cached stale response MAY be used to satisfy * the request, regardless of other freshness information.When used as a * request Cache-Control extension, its scope of application is the request * it appears in; when used as a response Cache-Control extension, its * scope is any request applicable to the cached response in which it * occurs.Its value indicates the upper limit to staleness; when the cached * response is more stale than the indicated amount, the cached response * SHOULD NOT be used to satisfy the request, absent other information. * In this context, an error is any situation that would result in a * 500, 502, 503, or 504 HTTP response status code being returned." * * http://tools.ietf.org/html/rfc5861 */ @Test public void testStaleIfErrorInResponseIsTrueReturnsStaleEntryWithWarning() throws Exception{ final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5, stale-if-error=60"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); HttpTestUtils.assert110WarningFound(result); } @Test public void testConsumesErrorResponseWhenServingStale() throws Exception{ final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5, stale-if-error=60"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); final byte[] body101 = HttpTestUtils.getRandomBytes(101); final ByteArrayInputStream buf = new ByteArrayInputStream(body101); final ConsumableInputStream cis = new ConsumableInputStream(buf); final HttpEntity entity = new InputStreamEntity(cis, 101, null); resp2.setEntity(entity); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); execute(req2); assertTrue(cis.wasClosed()); } @Test public void testStaleIfErrorInResponseYieldsToMustRevalidate() throws Exception{ final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5, stale-if-error=60, must-revalidate"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertTrue(HttpStatus.SC_OK != result.getCode()); } @Test public void testStaleIfErrorInResponseYieldsToProxyRevalidateForSharedCache() throws Exception{ assertTrue(config.isSharedCache()); final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5, stale-if-error=60, proxy-revalidate"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertTrue(HttpStatus.SC_OK != result.getCode()); } @Test public void testStaleIfErrorInResponseNeedNotYieldToProxyRevalidateForPrivateCache() throws Exception{ final CacheConfig configUnshared = CacheConfig.custom() .setSharedCache(false).build(); impl = new CachingExec(new BasicHttpCache(configUnshared), null, configUnshared); final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5, stale-if-error=60, proxy-revalidate"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); HttpTestUtils.assert110WarningFound(result); } @Test public void testStaleIfErrorInResponseYieldsToExplicitFreshnessRequest() throws Exception{ final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5, stale-if-error=60"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control","min-fresh=2"); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertTrue(HttpStatus.SC_OK != result.getCode()); } @Test public void testStaleIfErrorInRequestIsTrueReturnsStaleEntryWithWarning() throws Exception{ final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control","public, stale-if-error=60"); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); HttpTestUtils.assert110WarningFound(result); } @Test public void testStaleIfErrorInRequestIsTrueReturnsStaleNonRevalidatableEntryWithWarning() throws Exception { final Instant tenSecondsAgo = Instant.now().minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control", "public, stale-if-error=60"); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); HttpTestUtils.assert110WarningFound(result); } @Test public void testStaleIfErrorInResponseIsFalseReturnsError() throws Exception{ final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5, stale-if-error=2"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, result.getCode()); } @Test public void testStaleIfErrorInRequestIsFalseReturnsError() throws Exception{ final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); final ClassicHttpRequest req1 = HttpTestUtils.makeDefaultRequest(); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo, "public, max-age=5"); final ClassicHttpRequest req2 = HttpTestUtils.makeDefaultRequest(); req2.setHeader("Cache-Control","stale-if-error=2"); final ClassicHttpResponse resp2 = HttpTestUtils.make500Response(); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, result.getCode()); } /* * When present in an HTTP response, the stale-while-revalidate Cache- * Control extension indicates that caches MAY serve the response in * which it appears after it becomes stale, up to the indicated number * of seconds. * * http://tools.ietf.org/html/rfc5861 */ @Test public void testStaleWhileRevalidateReturnsStaleEntryWithWarning() throws Exception { config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .setAsynchronousWorkers(1) .build(); impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_OK, result.getCode()); boolean warning110Found = false; for(final Header h : result.getHeaders("Warning")) { for(final WarningValue wv : WarningValue.getWarningValues(h)) { if (wv.getWarnCode() == 110) { warning110Found = true; break; } } } assertTrue(warning110Found); Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any()); Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any()); } @Test public void testStaleWhileRevalidateReturnsStaleNonRevalidatableEntryWithWarning() throws Exception { config = CacheConfig.custom().setMaxCacheEntries(MAX_ENTRIES).setMaxObjectSize(MAX_BYTES) .setAsynchronousWorkers(1).build(); impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15"); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_OK, result.getCode()); boolean warning110Found = false; for (final Header h : result.getHeaders("Warning")) { for (final WarningValue wv : WarningValue.getWarningValues(h)) { if (wv.getWarnCode() == 110) { warning110Found = true; break; } } } assertTrue(warning110Found); Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any()); Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any()); } @Test public void testCanAlsoServeStale304sWhileRevalidating() throws Exception { config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .setAsynchronousWorkers(1) .setSharedCache(false) .build(); impl = new CachingExec(cache, executorService, ImmediateSchedulingStrategy.INSTANCE, config); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); resp1.setHeader("Cache-Control", "private, stale-while-revalidate=15"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("If-None-Match","\"etag\""); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getCode()); boolean warning110Found = false; for(final Header h : result.getHeaders("Warning")) { for(final WarningValue wv : WarningValue.getWarningValues(h)) { if (wv.getWarnCode() == 110) { warning110Found = true; break; } } } assertTrue(warning110Found); Mockito.verify(mockExecChain, Mockito.atLeastOnce()).proceed(Mockito.any(), Mockito.any()); Mockito.verify(mockExecChain, Mockito.atMost(2)).proceed(Mockito.any(), Mockito.any()); } @Test public void testStaleWhileRevalidateYieldsToMustRevalidate() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .setAsynchronousWorkers(1) .build(); impl = new CachingExec(cache, null, config); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, must-revalidate"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, must-revalidate"); resp2.setHeader("ETag","\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_OK, result.getCode()); boolean warning110Found = false; for(final Header h : result.getHeaders("Warning")) { for(final WarningValue wv : WarningValue.getWarningValues(h)) { if (wv.getWarnCode() == 110) { warning110Found = true; break; } } } assertFalse(warning110Found); } @Test public void testStaleWhileRevalidateYieldsToProxyRevalidateForSharedCache() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .setAsynchronousWorkers(1) .setSharedCache(true) .build(); impl = new CachingExec(cache, null, config); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, proxy-revalidate"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, proxy-revalidate"); resp2.setHeader("ETag","\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_OK, result.getCode()); boolean warning110Found = false; for(final Header h : result.getHeaders("Warning")) { for(final WarningValue wv : WarningValue.getWarningValues(h)) { if (wv.getWarnCode() == 110) { warning110Found = true; break; } } } assertFalse(warning110Found); } @Test public void testStaleWhileRevalidateYieldsToExplicitFreshnessRequest() throws Exception { final Instant now = Instant.now(); final Instant tenSecondsAgo = now.minusSeconds(10); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .setAsynchronousWorkers(1) .setSharedCache(true) .build(); impl = new CachingExec(cache, null, config); final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET", "/"); final ClassicHttpResponse resp1 = HttpTestUtils.make200Response(); resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15"); resp1.setHeader("ETag","\"etag\""); resp1.setHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET", "/"); req2.setHeader("Cache-Control","min-fresh=2"); final ClassicHttpResponse resp2 = HttpTestUtils.make200Response(); resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15"); resp2.setHeader("ETag","\"etag\""); resp2.setHeader("Date", DateUtils.formatStandardDate(now)); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp1); execute(req1); Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp2); final ClassicHttpResponse result = execute(req2); assertEquals(HttpStatus.SC_OK, result.getCode()); boolean warning110Found = false; for(final Header h : result.getHeaders("Warning")) { for(final WarningValue wv : WarningValue.getWarningValues(h)) { if (wv.getWarnCode() == 110) { warning110Found = true; break; } } } assertFalse(warning110Found); } } TestRequestProtocolCompliance.java000066400000000000000000000147201434266521000425510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestRequestProtocolCompliance { private RequestProtocolCompliance impl; @BeforeEach public void setUp() { impl = new RequestProtocolCompliance(false); } @Test public void testRequestWithWeakETagAndRange() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setHeader("Range", "bytes=0-499"); req.setHeader("If-Range", "W/\"weak\""); assertEquals(1, impl.requestIsFatallyNonCompliant(req).size()); } @Test public void testRequestWithWeekETagForPUTOrDELETEIfMatch() throws Exception { final HttpRequest req = new BasicHttpRequest("PUT", "http://example.com/"); req.setHeader("If-Match", "W/\"weak\""); assertEquals(1, impl.requestIsFatallyNonCompliant(req).size()); } @Test public void testRequestWithWeekETagForPUTOrDELETEIfMatchAllowed() throws Exception { final HttpRequest req = new BasicHttpRequest("PUT", "http://example.com/"); req.setHeader("If-Match", "W/\"weak\""); impl = new RequestProtocolCompliance(true); assertEquals(Collections.emptyList(), impl.requestIsFatallyNonCompliant(req)); } @Test public void testRequestContainsNoCacheDirectiveWithFieldName() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setHeader("Cache-Control", "no-cache=false"); assertEquals(1, impl.requestIsFatallyNonCompliant(req).size()); } @Test public void doesNotModifyACompliantRequest() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertTrue(HttpTestUtils.equivalent(req, wrapper)); } @Test public void upgrades1_0RequestTo1_1() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setVersion(HttpVersion.HTTP_1_0); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion()); } @Test public void downgrades1_2RequestTo1_1() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setVersion(new ProtocolVersion("HTTP", 1, 2)); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertEquals(HttpVersion.HTTP_1_1, wrapper.getVersion()); } @Test public void stripsMinFreshFromRequestIfNoCachePresent() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setHeader("Cache-Control", "no-cache, min-fresh=10"); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertEquals("no-cache", wrapper.getFirstHeader("Cache-Control").getValue()); } @Test public void stripsMaxFreshFromRequestIfNoCachePresent() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setHeader("Cache-Control", "no-cache, max-stale=10"); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertEquals("no-cache", wrapper.getFirstHeader("Cache-Control").getValue()); } @Test public void stripsMaxAgeFromRequestIfNoCachePresent() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setHeader("Cache-Control", "no-cache, max-age=10"); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertEquals("no-cache", wrapper.getFirstHeader("Cache-Control").getValue()); } @Test public void doesNotStripMinFreshFromRequestWithoutNoCache() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setHeader("Cache-Control", "min-fresh=10"); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertEquals("min-fresh=10", wrapper.getFirstHeader("Cache-Control").getValue()); } @Test public void correctlyStripsMinFreshFromMiddleIfNoCache() throws Exception { final HttpRequest req = new BasicHttpRequest("GET", "/"); req.setHeader("Cache-Control", "no-cache,min-fresh=10,no-store"); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); impl.makeRequestCompliant(wrapper); assertEquals("no-cache,no-store", wrapper.getFirstHeader("Cache-Control").getValue()); } } TestResponseCachingPolicy.java000066400000000000000000001115671434266521000416460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import java.util.Random; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.methods.HttpOptions; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestResponseCachingPolicy { private static final ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP", 1, 1); private ResponseCachingPolicy policy; private HttpResponse response; private HttpRequest request; private final int[] acceptableCodes = new int[] { HttpStatus.SC_OK, HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION, HttpStatus.SC_MULTIPLE_CHOICES, HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_GONE }; private Instant now; private Instant tenSecondsFromNow; private Instant sixSecondsAgo; @BeforeEach public void setUp() throws Exception { now = Instant.now(); sixSecondsAgo = now.minusSeconds(6); tenSecondsFromNow = now.plusSeconds(10); policy = new ResponseCachingPolicy(0, true, false, false); request = new BasicHttpRequest("GET","/"); response = new BasicHttpResponse(HttpStatus.SC_OK, ""); response.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); response.setHeader("Content-Length", "0"); } @Test public void testIsGetCacheable() { Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadCacheable() { policy = new ResponseCachingPolicy(0, true, false, false); Assertions.assertTrue(policy.isResponseCacheable("HEAD", response)); } @Test public void testResponsesToRequestsWithAuthorizationHeadersAreNotCacheableBySharedCache() { request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); Assertions.assertFalse(policy.isResponseCacheable(request,response)); } @Test public void testResponsesToRequestsWithAuthorizationHeadersAreCacheableByNonSharedCache() { policy = new ResponseCachingPolicy(0, false, false, false); request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); Assertions.assertTrue(policy.isResponseCacheable(request,response)); } @Test public void testAuthorizedResponsesWithSMaxAgeAreCacheable() { request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); response.setHeader("Cache-Control","s-maxage=3600"); Assertions.assertTrue(policy.isResponseCacheable(request,response)); } @Test public void testAuthorizedResponsesWithMustRevalidateAreCacheable() { request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); response.setHeader("Cache-Control","must-revalidate"); Assertions.assertTrue(policy.isResponseCacheable(request,response)); } @Test public void testAuthorizedResponsesWithCacheControlPublicAreCacheable() { request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); response.setHeader("Cache-Control","public"); Assertions.assertTrue(policy.isResponseCacheable(request,response)); } @Test public void testAuthorizedResponsesWithCacheControlMaxAgeAreNotCacheable() { request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); response.setHeader("Cache-Control","max-age=3600"); Assertions.assertFalse(policy.isResponseCacheable(request,response)); } @Test public void test203ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void test206ResponseCodeIsNotCacheable() { response.setCode(HttpStatus.SC_PARTIAL_CONTENT); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void test206ResponseCodeIsNotCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setCode(HttpStatus.SC_PARTIAL_CONTENT); response.setHeader("Cache-Control", "public"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void test300ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_MULTIPLE_CHOICES); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void test301ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_MOVED_PERMANENTLY); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void test410ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_GONE); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testPlain302ResponseCodeIsNotCacheable() { response.setCode(HttpStatus.SC_MOVED_TEMPORARILY); response.removeHeaders("Expires"); response.removeHeaders("Cache-Control"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testPlain303ResponseCodeIsNotCacheableUnderDefaultBehavior() { response.setCode(HttpStatus.SC_SEE_OTHER); response.removeHeaders("Expires"); response.removeHeaders("Cache-Control"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testPlain303ResponseCodeIsNotCacheableEvenIf303CachingEnabled() { policy = new ResponseCachingPolicy(0, true, false, true); response.setCode(HttpStatus.SC_SEE_OTHER); response.removeHeaders("Expires"); response.removeHeaders("Cache-Control"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testPlain307ResponseCodeIsNotCacheable() { response.setCode(HttpStatus.SC_TEMPORARY_REDIRECT); response.removeHeaders("Expires"); response.removeHeaders("Cache-Control"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testNon206WithExplicitExpiresIsCacheable() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Expires", DateUtils.formatStandardDate(Instant.now())); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testNon206WithMaxAgeIsCacheable() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Cache-Control", "max-age=0"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testNon206WithSMaxAgeIsCacheable() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Cache-Control", "s-maxage=0"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testNon206WithMustRevalidateIsCacheable() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Cache-Control", "must-revalidate"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testNon206WithProxyRevalidateIsCacheable() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Cache-Control", "proxy-revalidate"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testNon206WithPublicCacheControlIsCacheable() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Cache-Control", "public"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testNon206WithPrivateCacheControlIsNotCacheableBySharedCache() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Cache-Control", "private"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void test200ResponseWithPrivateCacheControlIsCacheableByNonSharedCache() { policy = new ResponseCachingPolicy(0, false, false, false); response.setCode(HttpStatus.SC_OK); response.setHeader("Cache-Control", "private"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testIsGetWithNoCacheCacheable() { response.addHeader("Cache-Control", "no-cache"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithNoCacheCacheable() { response.addHeader("Cache-Control", "no-cache"); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsGetWithNoStoreCacheable() { response.addHeader("Cache-Control", "no-store"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithNoStoreCacheable() { response.addHeader("Cache-Control", "no-store"); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsGetWithNoStoreEmbeddedInListCacheable() { response.addHeader("Cache-Control", "public, no-store"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithNoStoreEmbeddedInListCacheable() { response.addHeader("Cache-Control", "public, no-store"); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsGetWithNoCacheEmbeddedInListCacheable() { response.addHeader("Cache-Control", "public, no-cache"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithNoCacheEmbeddedInListCacheable() { response.addHeader("Cache-Control", "public, no-cache"); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsGetWithNoCacheEmbeddedInListAfterFirstHeaderCacheable() { response.addHeader("Cache-Control", "max-age=20"); response.addHeader("Cache-Control", "public, no-cache"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithNoCacheEmbeddedInListAfterFirstHeaderCacheable() { response.addHeader("Cache-Control", "max-age=20"); response.addHeader("Cache-Control", "public, no-cache"); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsGetWithNoStoreEmbeddedInListAfterFirstHeaderCacheable() { response.addHeader("Cache-Control", "max-age=20"); response.addHeader("Cache-Control", "public, no-store"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithNoStoreEmbeddedInListAfterFirstHeaderCacheable() { response.addHeader("Cache-Control", "max-age=20"); response.addHeader("Cache-Control", "public, no-store"); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsGetWithAnyCacheControlCacheable() { response.addHeader("Cache-Control", "max=10"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); response = new BasicHttpResponse(HttpStatus.SC_OK, ""); response.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); response.addHeader("Cache-Control", "no-transform"); response.setHeader("Content-Length", "0"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithAnyCacheControlCacheable() { policy = new ResponseCachingPolicy(0, true, false, false); response.addHeader("Cache-Control", "max=10"); Assertions.assertTrue(policy.isResponseCacheable("HEAD", response)); response = new BasicHttpResponse(HttpStatus.SC_OK, ""); response.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); response.addHeader("Cache-Control", "no-transform"); response.setHeader("Content-Length", "0"); Assertions.assertTrue(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsGetWithout200Cacheable() { HttpResponse response404 = new BasicHttpResponse(HttpStatus.SC_NOT_FOUND, ""); Assertions.assertFalse(policy.isResponseCacheable("GET", response404)); response404 = new BasicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, ""); Assertions.assertFalse(policy.isResponseCacheable("GET", response404)); } @Test public void testIsHeadWithout200Cacheable() { HttpResponse response404 = new BasicHttpResponse(HttpStatus.SC_NOT_FOUND, ""); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response404)); response404 = new BasicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, ""); Assertions.assertFalse(policy.isResponseCacheable("HEAD", response404)); } @Test public void testVaryStarIsNotCacheable() { response.setHeader("Vary", "*"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testVaryStarIsNotCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setHeader("Cache-Control", "public"); response.setHeader("Vary", "*"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testIsGetWithVaryHeaderCacheable() { response.addHeader("Vary", "Accept-Encoding"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testIsHeadWithVaryHeaderCacheable() { policy = new ResponseCachingPolicy(0, true, false, false); response.addHeader("Vary", "Accept-Encoding"); Assertions.assertTrue(policy.isResponseCacheable("HEAD", response)); } @Test public void testIsArbitraryMethodCacheable() { Assertions.assertFalse(policy.isResponseCacheable("PUT", response)); Assertions.assertFalse(policy.isResponseCacheable("get", response)); } @Test public void testIsArbitraryMethodCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request = new HttpOptions("http://foo.example.com/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setCode(HttpStatus.SC_NO_CONTENT); response.setHeader("Cache-Control", "public"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToRequestsWithNoStoreAreNotCacheable() { request.setHeader("Cache-Control","no-store"); response.setHeader("Cache-Control","public"); Assertions.assertFalse(policy.isResponseCacheable(request,response)); } @Test public void testResponsesWithMultipleAgeHeadersAreNotCacheable() { response.addHeader("Age", "3"); response.addHeader("Age", "5"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testResponsesWithMultipleAgeHeadersAreNotCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setHeader("Cache-Control", "public"); response.addHeader("Age", "3"); response.addHeader("Age", "5"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesWithMultipleDateHeadersAreNotCacheable() { response.addHeader("Date", DateUtils.formatStandardDate(now)); response.addHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo)); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testResponsesWithMultipleDateHeadersAreNotCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setHeader("Cache-Control", "public"); response.addHeader("Date", DateUtils.formatStandardDate(now)); response.addHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo)); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesWithMalformedDateHeadersAreNotCacheable() { response.addHeader("Date", "garbage"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testResponsesWithMalformedDateHeadersAreNotCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setHeader("Cache-Control", "public"); response.addHeader("Date", "garbage"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesWithMultipleExpiresHeadersAreNotCacheable() { response.addHeader("Expires", DateUtils.formatStandardDate(now)); response.addHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testResponsesWithMultipleExpiresHeadersAreNotCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setHeader("Cache-Control", "public"); response.addHeader("Expires", DateUtils.formatStandardDate(now)); response.addHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesWithoutDateHeadersAreNotCacheable() { response.removeHeaders("Date"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testResponseThatHasTooMuchContentIsNotCacheable() { response.setHeader("Content-Length", "9000"); Assertions.assertFalse(policy.isResponseCacheable("GET", response)); } @Test public void testResponseThatHasTooMuchContentIsNotCacheableUsingSharedPublicCache() { policy = new ResponseCachingPolicy(0, true, false, false); request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); response.setHeader("Cache-Control", "public"); response.setHeader("Content-Length", "9000"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesThatAreSmallEnoughAreCacheable() { response.setHeader("Content-Length", "0"); Assertions.assertTrue(policy.isResponseCacheable("GET", response)); } @Test public void testResponsesToGETWithQueryParamsButNoExplicitCachingAreNotCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheable() { request = new BasicHttpRequest("HEAD", "/foo?s=bar"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToGETWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("GET", "/foo?s=bar"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheable() { policy = new ResponseCachingPolicy(0, true, false, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() { request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSetting() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("GET", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSetting() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() { policy = new ResponseCachingPolicy(0, true, false, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpires() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("GET", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpires() { policy = new ResponseCachingPolicy(0, true, true, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Via", "1.0 someproxy"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() { request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Via", "1.0 someproxy"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "1.0 someproxy"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() { policy = new ResponseCachingPolicy(0, true, false, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "1.0 someproxy"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() { policy = new ResponseCachingPolicy(0, true, true, true); request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "1.0 someproxy"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() { policy = new ResponseCachingPolicy(0, true, true, true); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "1.0 someproxy"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "HTTP/1.0 someproxy"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() { policy = new ResponseCachingPolicy(0, true, false, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "HTTP/1.0 someproxy"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() { policy = new ResponseCachingPolicy(0, true, true, true); request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "HTTP/1.0 someproxy"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() { policy = new ResponseCachingPolicy(0, true, true, true); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "HTTP/1.0 someproxy"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void getsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "1.1 someproxy"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void headsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() { policy = new ResponseCachingPolicy(0, true, false, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader("Via", "1.1 someproxy"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void notCacheableIfExpiresEqualsDateAndNoCacheControl() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(now)); response.removeHeaders("Cache-Control"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void notCacheableIfExpiresPrecedesDateAndNoCacheControl() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)); response.removeHeaders("Cache-Control"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void test302WithExplicitCachingHeaders() { response.setCode(HttpStatus.SC_MOVED_TEMPORARILY); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Cache-Control","max-age=300"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void test303WithExplicitCachingHeadersUnderDefaultBehavior() { // RFC 2616 says: 303 should not be cached response.setCode(HttpStatus.SC_SEE_OTHER); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Cache-Control","max-age=300"); Assertions.assertFalse(policy.isResponseCacheable(request, response)); } @Test public void test303WithExplicitCachingHeadersWhenPermittedByConfig() { // HTTPbis working group says ok if explicitly indicated by // response headers policy = new ResponseCachingPolicy(0, true, false, true); response.setCode(HttpStatus.SC_SEE_OTHER); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Cache-Control","max-age=300"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void test307WithExplicitCachingHeaders() { response.setCode(HttpStatus.SC_TEMPORARY_REDIRECT); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Cache-Control","max-age=300"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } @Test public void otherStatusCodesAreCacheableWithExplicitCachingHeaders() { response.setCode(HttpStatus.SC_NOT_FOUND); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Cache-Control","max-age=300"); Assertions.assertTrue(policy.isResponseCacheable(request, response)); } private int getRandomStatus() { final int rnd = new Random().nextInt(acceptableCodes.length); return acceptableCodes[rnd]; } } TestResponseProtocolCompliance.java000066400000000000000000000057361434266521000427260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestResponseProtocolCompliance { private ResponseProtocolCompliance impl; @BeforeEach public void setUp() { impl = new ResponseProtocolCompliance(); } private void setMinimalResponseHeaders(final HttpResponse resp) { resp.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); resp.setHeader("Server", "MyServer/1.0"); } private HttpResponse makePartialResponse(final int nbytes) { final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); setMinimalResponseHeaders(resp); resp.setHeader("Content-Length","" + nbytes); resp.setHeader("Content-Range","0-127/256"); return resp; } @Test public void throwsExceptionIfOriginReturnsPartialResponseWhenNotRequested() throws Exception { final HttpGet req = new HttpGet("http://foo.example.com/"); final HttpRequest wrapper = BasicRequestBuilder.copy(req).build(); final int nbytes = 128; final HttpResponse resp = makePartialResponse(nbytes); Assertions.assertThrows(ClientProtocolException.class, () -> impl.ensureProtocolCompliance(wrapper, req, resp)); } } TestWarningValue.java000066400000000000000000000245301434266521000400060ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache; import java.time.Instant; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestWarningValue { @Test public void testParseSingleWarnValue() { final Header h = new BasicHeader("Warning","110 fred \"stale\""); final WarningValue[] result = WarningValue.getWarningValues(h); Assertions.assertEquals(1, result.length); final WarningValue wv = result[0]; Assertions.assertEquals(110, wv.getWarnCode()); Assertions.assertEquals("fred", wv.getWarnAgent()); Assertions.assertEquals("\"stale\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); } @Test public void testParseMultipleWarnValues() { final Header h = new BasicHeader("Warning","110 fred \"stale\", 111 wilma \"other\""); final WarningValue[] result = WarningValue.getWarningValues(h); Assertions.assertEquals(2, result.length); WarningValue wv = result[0]; Assertions.assertEquals(110, wv.getWarnCode()); Assertions.assertEquals("fred", wv.getWarnAgent()); Assertions.assertEquals("\"stale\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); wv = result[1]; Assertions.assertEquals(111, wv.getWarnCode()); Assertions.assertEquals("wilma", wv.getWarnAgent()); Assertions.assertEquals("\"other\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); } @Test public void testMidHeaderParseErrorRecovery() { final Header h = new BasicHeader("Warning","110 fred \"stale\", bogus, 111 wilma \"other\""); final WarningValue[] result = WarningValue.getWarningValues(h); Assertions.assertEquals(2, result.length); WarningValue wv = result[0]; Assertions.assertEquals(110, wv.getWarnCode()); Assertions.assertEquals("fred", wv.getWarnAgent()); Assertions.assertEquals("\"stale\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); wv = result[1]; Assertions.assertEquals(111, wv.getWarnCode()); Assertions.assertEquals("wilma", wv.getWarnAgent()); Assertions.assertEquals("\"other\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); } @Test public void testTrickyCommaMidHeaderParseErrorRecovery() { final Header h = new BasicHeader("Warning","110 fred \"stale\", \"bogus, dude\", 111 wilma \"other\""); final WarningValue[] result = WarningValue.getWarningValues(h); Assertions.assertEquals(2, result.length); WarningValue wv = result[0]; Assertions.assertEquals(110, wv.getWarnCode()); Assertions.assertEquals("fred", wv.getWarnAgent()); Assertions.assertEquals("\"stale\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); wv = result[1]; Assertions.assertEquals(111, wv.getWarnCode()); Assertions.assertEquals("wilma", wv.getWarnAgent()); Assertions.assertEquals("\"other\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); } @Test public void testParseErrorRecoveryAtEndOfHeader() { final Header h = new BasicHeader("Warning","110 fred \"stale\", 111 wilma \"other\", \"bogus, dude\""); final WarningValue[] result = WarningValue.getWarningValues(h); Assertions.assertEquals(2, result.length); WarningValue wv = result[0]; Assertions.assertEquals(110, wv.getWarnCode()); Assertions.assertEquals("fred", wv.getWarnAgent()); Assertions.assertEquals("\"stale\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); wv = result[1]; Assertions.assertEquals(111, wv.getWarnCode()); Assertions.assertEquals("wilma", wv.getWarnAgent()); Assertions.assertEquals("\"other\"", wv.getWarnText()); Assertions.assertNull(wv.getWarnDate()); } @Test public void testConstructSingleWarnValue() { final WarningValue impl = new WarningValue("110 fred \"stale\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("fred", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithIPv4Address() { final WarningValue impl = new WarningValue("110 192.168.1.1 \"stale\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("192.168.1.1", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithHostname() { final WarningValue impl = new WarningValue("110 foo.example.com \"stale\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("foo.example.com", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithHostnameAndPort() { final WarningValue impl = new WarningValue("110 foo.example.com:8080 \"stale\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("foo.example.com:8080", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithIPv4AddressAndPort() { final WarningValue impl = new WarningValue("110 192.168.1.1:8080 \"stale\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("192.168.1.1:8080", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithPseudonym() { final WarningValue impl = new WarningValue("110 ca$hm0ney \"stale\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("ca$hm0ney", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithTextWithSpaces() { final WarningValue impl = new WarningValue("110 fred \"stale stuff\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("fred", impl.getWarnAgent()); Assertions.assertEquals("\"stale stuff\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithTextWithCommas() { final WarningValue impl = new WarningValue("110 fred \"stale, stuff\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("fred", impl.getWarnAgent()); Assertions.assertEquals("\"stale, stuff\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithTextWithEscapedQuotes() { final WarningValue impl = new WarningValue("110 fred \"stale\\\" stuff\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("fred", impl.getWarnAgent()); Assertions.assertEquals("\"stale\\\" stuff\"", impl.getWarnText()); Assertions.assertNull(impl.getWarnDate()); } @Test public void testConstructWarnValueWithAscTimeWarnDate() throws Exception { final WarningValue impl = new WarningValue("110 fred \"stale\" \"Sun Nov 6 08:49:37 1994\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("fred", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); final Instant target = DateUtils.parseStandardDate("Sun Nov 6 08:49:37 1994"); Assertions.assertEquals(target, impl.getWarnDate()); } @Test public void testConstructWarnValueWithRFC850WarnDate() throws Exception { final WarningValue impl = new WarningValue("110 fred \"stale\" \"Sunday, 06-Nov-94 08:49:37 GMT\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("fred", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); final Instant target = DateUtils.parseStandardDate("Sunday, 06-Nov-94 08:49:37 GMT"); Assertions.assertEquals(target, impl.getWarnDate()); } @Test public void testConstructWarnValueWithRFC1123WarnDate() throws Exception { final WarningValue impl = new WarningValue("110 fred \"stale\" \"Sun, 06 Nov 1994 08:49:37 GMT\""); Assertions.assertEquals(110, impl.getWarnCode()); Assertions.assertEquals("fred", impl.getWarnAgent()); Assertions.assertEquals("\"stale\"", impl.getWarnText()); final Instant target = DateUtils.parseStandardDate("Sun, 06 Nov 1994 08:49:37 GMT"); Assertions.assertEquals(target, impl.getWarnDate()); } } memcached/000077500000000000000000000000001434266521000356035ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cacheTestPrefixKeyHashingScheme.java000066400000000000000000000036231434266521000436470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestPrefixKeyHashingScheme { private static final String KEY = "key"; private static final String PREFIX = "prefix"; private PrefixKeyHashingScheme impl; private KeyHashingScheme scheme; @BeforeEach public void setUp() { scheme = storageKey -> { assertEquals(KEY, storageKey); return "hash"; }; impl = new PrefixKeyHashingScheme(PREFIX, scheme); } @Test public void addsPrefixToBackingScheme() { assertEquals("prefixhash", impl.hash(KEY)); } } TestSHA256HashingScheme.java000066400000000000000000000031271434266521000426100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/memcached/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cache.memcached; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class TestSHA256HashingScheme { @Test public void canHash() { final SHA256KeyHashingScheme impl = new SHA256KeyHashingScheme(); final String result = impl.hash("hello, hashing world"); assertTrue(result != null && !result.isEmpty()); } } schedule/000077500000000000000000000000001434266521000344265ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/implTestExponentialBackingOffSchedulingStrategy.java000066400000000000000000000050511434266521000460630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.schedule; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestExponentialBackingOffSchedulingStrategy { private ExponentialBackOffSchedulingStrategy impl; @BeforeEach public void setUp() { impl = new ExponentialBackOffSchedulingStrategy( ExponentialBackOffSchedulingStrategy.DEFAULT_BACK_OFF_RATE, ExponentialBackOffSchedulingStrategy.DEFAULT_INITIAL_EXPIRY, ExponentialBackOffSchedulingStrategy.DEFAULT_MAX_EXPIRY ); } @Test public void testSchedule() { Assertions.assertEquals(TimeValue.ofMilliseconds(0), impl.schedule(0)); Assertions.assertEquals(TimeValue.ofMilliseconds(6000), impl.schedule(1)); Assertions.assertEquals(TimeValue.ofMilliseconds(60000), impl.schedule(2)); Assertions.assertEquals(TimeValue.ofMilliseconds(600000), impl.schedule(3)); Assertions.assertEquals(TimeValue.ofMilliseconds(6000000), impl.schedule(4)); Assertions.assertEquals(TimeValue.ofMilliseconds(60000000), impl.schedule(5)); Assertions.assertEquals(TimeValue.ofMilliseconds(86400000), impl.schedule(6)); Assertions.assertEquals(TimeValue.ofMilliseconds(86400000), impl.schedule(Integer.MAX_VALUE)); } } TestImmediateSchedulingStrategy.java000066400000000000000000000036401434266521000435630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.schedule; import org.apache.hc.client5.http.schedule.SchedulingStrategy; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestImmediateSchedulingStrategy { private SchedulingStrategy impl; @BeforeEach public void setUp() { impl = new ImmediateSchedulingStrategy(); } @Test public void testSchedule() { Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, impl.schedule(0)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, impl.schedule(1)); Assertions.assertEquals(TimeValue.ZERO_MILLISECONDS, impl.schedule(Integer.MAX_VALUE)); } } schedule/000077500000000000000000000000001434266521000334655ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/httpTestConcurrentCountMap.java000066400000000000000000000034651434266521000407710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/java/org/apache/hc/client5/http/schedule/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.schedule; import static org.hamcrest.MatcherAssert.assertThat; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public class TestConcurrentCountMap { private static final String IDENTIFIER = "some-identifier"; private final ConcurrentCountMap map = new ConcurrentCountMap<>(); @Test public void testBasics() { map.increaseCount(IDENTIFIER); map.increaseCount(IDENTIFIER); assertThat(map.getCount(IDENTIFIER), CoreMatchers.equalTo(2)); map.resetCount(IDENTIFIER); assertThat(map.getCount(IDENTIFIER), CoreMatchers.equalTo(0)); } } httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resources/000077500000000000000000000000001434266521000261155ustar00rootroot00000000000000ApacheLogo.httpbytes.serialized000066400000000000000000001045311434266521000341460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-type: text/html Cache-control: public, max-age=31536000 Content-Length: 34983 hc-sk: xyzzy hc-resp-date: 2611108800000 hc-req-date: 165214800000 PNG  IHDRpK6bKGD IDATxy|e;s[irtg_E"*n,(7pAŋ( ¦ BY-&m{ڜ$gqB.^|ygwΜg&dI&dI&dI&@{L2$R Mq|ԩk9'dI&ly`!i :}yƴ_3^&dI&f[5 \\ TMds--gl\`Ⱦӧr"dI&_Wi0Wi۬9̞Wq]w{;޺765?[؅kw5$L2?!?O=T蠃/B>.бsΙW01{_h)//oi=kZbr'He l`7SRgk U:M{I&dIIP3/_~^>gΜNڔ32tLO7ws-szxiM]G2@K.^qEz "ej?in*p3MuT:jQ/Z#/]KRwŽ$y8Z}M$L2$o&Yfȓ˗/xQRxb@[vcUzl6O_Y[{ի~ZxCkds~Ybű##Ox;Y}ENk:aœ97=Vd{ohR^KD8$L2?1톪+V8ndK?x~[Z() x_xS_rʴ6HUݶLz 73gμGynͿ9:0\B@6b]U,Z[ 4E\JNC!D*cX !BjcHIxUjۢU9\iXr(ZU[6=_91Xy:bz v!g-S>d/Hz 2n忩1Z78_h٫VZ1\58ޮ߈5MڰqAZ:.2ߑϝ;w

O1L?bIv]k$N{]W3$ 얩d޼y.[ZQ:J,X… +N؊~{LJJJiM$vaI"X JمL4QBl)%/e$ %LSN':3/93JF"xXB 9nuz0G,[C4˓nN.5vT2*"Diy.k<[zOutV)/xRAk^DlQ/n6|`tYO۞cĭMMGwu*}$G̙ŋ-~WD>pGsGC=5\#>, T & G"tKiip Gy]^Wd 1&if(!VDRh#)Qk3ͼ#I6jSN5CR4( |߇Aq#BX+RZ\IYzR>qg WkSE#.)+ÃsjP3{{ ~_v=8JyY@Ϭ^0_=y^D/aE@1Hh-=֘owuuMHYġc{Ibo8oٲeȯE^UG.X HGG7pPUyޫ̊*˘nS-% "A a3HDLR(4gWJHr3($q,3kJ(|BZPK4)'lJIDt MNMb1& ]of]U,+_oo <"qŠϗ"/~ س̗Ԟ>Iv ntT6rl{sa==n3ikJp˺]4h=nUOu͞=߿L`R@M]Aǜ9s sKl2sLoGy~R[[ƘFAP՗[*KrbSW6D(nC q1@x"_BD4g*Q=3/=/:wEq\u}jP!#yqp*Y?x0Wg3guo5(;-ze~Ӿ?u?: @k}. 1DvG[s><9ʅbm|4Rw'>QD"1 Ү%|r>rUe455jje***>&guկ~WtJ5cO}XHS,r"$ſC!!GAIhЭO&Id?ҀY |pp\ؚ@O25'9gUߪ]!זH6ro/_L@^E9pu5'6!wҲQ=+AZ}(B[sb4p[շ=E. 3Iq| CblknteOس(_aK-Em "zNmMʞFPns7Nn-yuk$^^7Π})p(UqַKṂ+Aͱ%L2' ?$Aa eq:Giv4H!OYLj-פy2kAFlh_M|Kv^C);߻toqٰ8o-i'c͕%.ae{lInm]6+1 v dVͬ+8mn\.u;u b;6~E+"cF_VЏ7@y1`CB {1ion?JZx[ı[ZuNG+GS|c-ԡgM-tvjwNnmm0Z`fȧűnO$}^ HD9][@9%m͉+kkO{:WFu[m Ƿ5|weOEL8*Mo=玏ܵfMwW`iԆ)Ï{z,^8Gx9|_ Éu{9mDBq66uңx =ReD_F?xSb" x.Y!Ig%Pًrzm1Ģpa#l4ܘkA\4dzreɩї,q*?8ɖ;`rbӡ,_u*Zb ȔH+5nh*Ks+#1'7ܜ}rU;,[jժ޶Dr/ pm͏ fZ: r1mڴm͉gD߉Pi7pp>ȋJ P Lv\4րD"QꝻc+O㩜\<րD"Q+X>pX B4Ɖzi";b`kW~gҊKy E,t]|tuTdr)[k+V=g e ScpXnf\% f]Vr@~f3ufn~3-,YBˠ?!gɓe8zoGl`(h#!ndJFj^$Z=Pc\1DVu E|\ IDAT. +sL%U ogrȴOtԚgI~tM3nx]CNwD XqkX::GaeyeU]sQ[cۍ8UPф&U]wA% {Nlrn'ըsw2KD;{{M3XAjǹG/Xe]]]O be53HM1yUD"@|Q9U#[ZZU9ngk v{ 7|Tadp=p#---bul?rx&lkk9eO&_=5mڴjܳnq'2a=38ưnuZG`%`3yZr'>uS Ѣ 6 /!rReRb߻YVg3H} x @ &dK(uIi?G]^~$)Cu63 Kx0$W:8?$Q\k G_ 筴|TeEVsAiGJ19X \Zy#k[Rk' K 2y..Yϩ~UBCŢ_6e1ٳ7:Y.7UlY~Z;w 6m}^tZ=|?𺵁Zƥsu?y{/v[vA+W\֜xb1L&N@-C8\ksg~=Ef0=iO-ߐKbMJqKi"g= dLm:rXǶjժm͉灱9͚6v;Θ0icc}DxS7lMΟ~{_Zd0ӏ=,;tDdY 5c), X6/!jPPp(QaDngڋJif6ͱ(c㖁B?&c^ @?@xX#qBDއS,Vր-bJv?I'*5 R @ $5kY ꌡZˋ0 QOý\ߛb@Z*IK"=\5%@mlCTP oGftk (z=o++QC> x+.'9o.71O dй5|xCyVIG++s_@<04m qS r3%DJ,]qv땑vMW+KR5p$?g<]'S.$ +jN89JXcĀ0>ÏT}r{Ɋ囗{M;c?(n lj#*q+{[ [q҇ \Z}>;F}W/:cOQwM쎓JwD"Eul?yߣ ^' ,YQWkllp8F ㆦ[k_k:0A*cΛ9s毗/_~ d??ﻰsCe%N%_qÔaC#KaH aU+9)JQ@Qn,+ b_RӢ*1?_=5SM/af2qxeQh+a,濢p C?mR*L!/>B76닚 X/*V~饸<@98(xx|5d~B]ͯ_\۟&s.݇CU9ΐSݡZrƶcwtL8hǸCbh7} m)VmG'|׈q=%S;َ~w΄svè|@GGǻN9ZUEYf)"yW ~kEGyRU<3Ȱe#=aB tkXY8X=pاB,V@|U@4救 :(ȕ%Rҷ@|x$7[ Kap\ݔ8eĥ5ϦHqj(1tKi& 0fwG@2yD raDJr|8 Q2[yx' U&(Ggk" GiH}nv9ǎ[U~ۣSew,i@SA%G刀L CdkR#nԈ`*6@9b#zNyRǥEc =͢Ä%JVd!|;Zda1 X%on Zp(1Sr54?I-,2 9CU[X~ZϽLC)!fPm#E{/ExN%-PuW-XnmP5 4wqKr@ZEeTmMG+kDbH橵?~gk#y%nEU$ܫLnhM> jDk#-ՙv?;3D?g9ko/ŖoU j6vA8)t ,}AEXFEn;/qR/7E%ɘmqb:Bu_E__߽=wBTub. /~I$"&Vܡk,^ChXe?b*ԆqՒ#Y iiF0xz?P xdQ[s~*k&sTxvA"e\ jt$^7u1W^?۽͞=Q"bbXo^\xŷ8$>crp*sr JN PPc :l6)%#X\S:訐|n`:8x8X m !.,G2l:uZ#/i ϧ2|eigWn*}U4M}}ҼΡKߐkxM$ ǿ!Go|aGQ8k[֫iiٴ8TYyƈz(!n>>=ezא?#'"vٲe2\U T֕|?kUd^x."jv(gO\ʇMY \ݏvJa0|/KjƌhL]o6ʈK T&*1vږQKIk/X/E;f iZCZW$- M<pӣS 2 R0Y)<+Q,yoBUl i2;Q\"t b8q"nN Aȉ0.F֍|'VDW0%~ T߉<{<&8tJ⺌ӈD `| +뇳|,ɛKH'PZKfRox~ܲڊVqҁaXgyv?M 0w߳.TOUD/?6yM^sDb>KB9j֦#7FD>S_oUXq{ȃ<跑x8}}}-G'DחmkN< =ÿ9s ,Y*  N-o| a!}jx͙ C>k.R{7geV5C4(Ie\X#ZTDȘaȨykFu: pd.Q'tZ7*莳XZAӝRԍTi3Brީs)QX#TF@=Y-P0>yrx]zd}>N / ( V n@LOf\BVK^<3dNpʨe==bm0MqU4' {3#i)QF|[N+[ʢIaQBfX J_l? K?1/\Y!;>~;Nbh{bE6lHFr~ 0N+ x`(y; d ݑ *ىe& UInP]u|vWw4G=W z~̈́,t?=ahoj]gon(S8{eO$P[g}:::>d+ =?ثh>,TvsTUϽɇ_z37]-{8((LChi)Eqn̏/3Wʱ"+a KckJp`Ia*dĊ'KfL*6}4t,3߇7ݟpmi>{`1o婮Vw#g8l,Qx(1z5$aSm5a*C)<?/FŐڻ8҄pKv^k Oj)(ngJ&F7}tkw`@x<v3#9RDH3I ?)ɹicLVܝTH"H/Jg嫕[p$rFOq<0?5{n 3g``|ouyyc&{#֮]aC.7gt\(sK(޹B+j>4 jnCm3jR*LUAb퉣HUTºp 6RYQn hv~άPPzbOvw5@FЯtp p1qFd@fuZOSdǧmr*x^4SlJ&|Ǚ檖{@>Ճ1)f͚%Kz/PՅ"?BkXN4}䴹y}mZsT3GK' q&8s$\hۂ̪tEYH>?j_@պ}k9%Yy2o„_v^`U(¥жP!pk h6#ε'Px7y / g*ON\ŋ##^,.+%VHtI7'_'ߙ!`%I/**KJ'),%>zI%)Ńz)Nĉ&?3tFq3V)(ߤnm|%P^Ho/d8ClC"bM貽LƇ \`Kk'4ŖfJ*p|I9m'^A2Bw"T9xj /P}qOr#^m$ӡ!|n7s2J_Ogŕ|PQ. 0L}2~h^'d@'sKJ|ȦfA&hHQl"KWi3cbgRb|b(Q5ᘽ*䣇M IDAT7ECS_\|j'b7YPx&~xѾO-$l3ݺ؟<@3 V4v޷~Us̾e_VR-vnL }İCKJKmo۸qE4Hc m䉆ƜDo8g^B&X=ڧd[ZZZzǀݞ5A,[,D:H%׽Ӄj֦ɤ烈~=ŧO^*w\0b f9W2g,/W㱅.Ff@E;\;%c-YMs*bH)?wzH<Ͽ&S-L@X{@f? _X~@CSP=+Q4LJ%Excfr߃+>8.u_R0-0&`N n(* 9%5(u\G-kunbڄUMk뾠nuןQGp x#S/*(`PrLcsc"Tw0EHrJ p,3:&Sfx0wܡf'IT 2舚\mm/=b"JMUl+٥7qpHn}StL(5%K^XU;Esu6l\]v=7nƯeF kcqaP3b n^̯fPwZB;H 'ﶔ0=.%y\Kk(~ |`5Kش5*MpO*\/RL?;ml$ro)]1.k2yO*=ieŅ2&\I,sꉘ lcTIFHSuy}dAj~_6`)2?r*CNEn^&^ib M[` KGWDru(*R180j"3(f\| 2jKڹ7KTc=Ǿwfϛ]Ϻez4译HLՌS9;|`UA~_8(](FhA:lgMUfR55As |L1m‰u?I*Tg7*GqEk}4z-Xlǀϋ$Ujl6ִX_:EO9q,}{ W~%n8r枏k!^[b }c9vFe|9e*;\|b~ױ5+)kg^0_2؄˗ sKUo{QC~Ų|/M{ \C\?=Y ~*Yc&SEb?{h8&+IeS-e۸$YI9D'@I oCLogy-lp* Pxd6]|86-yPXaW5|܆ 8ͬ4p='ZU}&ʿ7q;n:F'ۛ;x=$KZL/'rY)Ʃ#ʙ>5i^Mnn୥\  -E՞;_b+p@-mNkkT8PVEbq{ kU80Y&ȟ\#46623{{UZGێ<=EE"'o+IUpWQo3sUJ]Cs#d#X#QTm#3P9 ü+@TY|5"r"fpNJ W_=}~[{iyzlLWoJW$-Ĕo\ O(ޛ݄pOރ XΜ{z81V?''\\8: h SrfWN |WiLqVKSth5$9_"$PN(lիSRy((g'`%|g#uXC |[X$G*y_dK߱h#T)<}/$ʘ#(S)94Wzi|UgP/ {Eh\[7 <Vij}Coa* @3oH2Ҷ9Φ- .|jz{O>ܚoIޙ1  O\ 붵ab4)?BPqGNƊO`mhi7c`5 jL OUIم{hGpFJ)0IDvD7,sEF1ǍT)媲Q,˭ - u$KwCU>#Y^Up^w*hCOz5m{1,y\g99X1Ÿ8ݴ?mw Y/smo s |#9S(^璓xO!DF:Yη3J>SX,7;xE[7XҤ^l\RHpw-K a[ .]͵KL?{ُ2+\ȋ/oŕ;Slǣy:q6&; g0IW9qsK98E9p-W&J?ֈ6 YLCn.~[gP-I8ؗ{ eɴYK3 7/{˥*5da$':QpVr sZHj-]\e9&-U7uII ;ͽ3dBpRNNl]R4yL?.T˗_4X>(#IE%> vސR07x“Dh:n`*Oi?v#UJ&/ Ȃ?~(7%沽:TR9—8rLop*9uL|ZoѴ p+4?ϑ '8o޼ ލ qD++0aILuÀ=+{ t7l,~7M${u-6{,wR|gUoNޤ{{3f1q8|=%Y홝LN61mkå/5qK43BʼTvO[)^ohMWkpmoʍVfzĒJ G;yU;pg=):4m`n/uci񭚘ybftkSI]ĉNߎ˅s7rA.w~旀•]7k1ylW3k&(8{ѯVGN>$ i J4O]<ϻC)."v˖-k׮%L2m4&N=?]Aqʦ˙W^KYؤIe83eʔ^Ijw)hLnu@8{"jh/JUx>T5b-m*$H )?iM2تFUz 2֚&͠_5?(q=Ke5fWذvuD2 I hN@1^lWόp'g_ZZ8꣏x{z{8CTZ|hiA|yq5wȖP1 Z@onwݪjI '.^ۡPhG"}3.Pe˖;sf1>)ֶc'cJUZ[,"YVD2{_E5+?]_ cԐscybzE!/B/| ɄLxgC3L1ƸdF42Ƹ:8`֬ř&cLO} w^#"6̙3gޭ#ƒ%K||(`kTb"""D$/3hTݤ54'cء"kuD/%H+FHk)^UM&LYJU 6Rե"X,7s~ `yq͉DB`_."Xk1_֞ Tcb>mYjY3k֬1xSʼϺhѦ tck]ܰ۴3* NXy}4(+z䍆;="87 a0ze}SnK%yyy%.2Ɯ TcLLU[1߷'"sRc1#"28q־8NVD$e zkmC!t U|7B1dcʕ1lei ޱz'\-Y"h?( TUժkfe9OȂ]خqg:L@ՖŤό,\`~"#dD}5F~(xWF38d*!{2]g 4f*^Y\9t^GdCCCvmm/}|Sҹk̑vq4vp/hhNT;1{xR4twcW1fJU?(9}K6,z r) |sˆMH 9{ch!H"kDYW{JuQyC pUՠ%x|&7&~&\U3:NH:iY[ܜB}R;5b֓<\0wd_OmmmI+I#f8Ln@0 S⾿#QV'Z֘8Gx,3,G-p6`"U腮&c(n=5Q-־jQ:Á/{o kO%w9Cl&2]I'y;p0kxېNJ,bnZpbk ۼOwg -w^bZAo)ZupFcGש>FbUÁG䅫pB[aP;\F4];Ns707pJb^Z~%coE;;k!v5bk 5;]M$8x^*Hok(k"jueA% 4>F4/JnN9eމ ex { IDATmQr6+T\dz|2$R չXeB$Y>2AzyljV`]y*`H ߂ F-NwJVOm _~sqLGf/'8??=bha( k[}SX:U֚@ "dC>Z< 9ʼ8w;o,^ȞP)3 8-{e=pCPh_Uat]  }Χ:X$ȃQ\qvG8H+#A̯}c>'Hcmc< *X B_=&DYUf!|,?DB573tt_YSLX ilt,l|<0l "TTUU,Trxֹe:./+6j$Pik#;Ђ7!Ri1U~dYƏ2PT0t.ZNaKcY8)2x?~ jkk};Ģ<ȫ`-ȡ ]Hd>^SS,KLb,73$@sfpvxVezIrmqTR㌦@-tb }lJ<$X|V8R,Mx۹78ZBѣ1PFFUFGY(B/w"ཪͯf(F礁cuuDl^K:MrHU mxdk@`%pG]+W5ҒP< !ʡP>8#ȀN$gݏ~-]xZ{--hAKR@F41C&z}ph5*\;O@~GpT,vHe#f6Pxàg75WV[[uvߍka8 W"牓'?#Ff8mHЩ*GuGq.!C ,B*'p`2U6Yw}ֈT!3JZ9.}:vt!UhVd4u9qLaK(:ר.rY{W66>mS#Y4xW_}|xʄHUܺ%}E*e=:I XaIW( P* V-RK#-@5"D!=gf& ,;~Gc=?krP@w3E>%MMfx7_b*xGКpSG ],YW7&+ /h~U$~Ad{F@GHߡ 7@dC.1(ғL>(`0gGu%0Pa745^X]"c[Mf4Tգ%@́466_z,{ (+2|KdT0CCH_݊1K v vCXVD"?\T vbf Z f=ֺ6תGhQ`6p1L@a3ug3|vK"zK>r (}#HHV} N44@054H$dww#L 0]T˭J"SUt^y& 07u%wn.9|\@Dds6h4 Wwo c64G |K1AurpE_ d;QafQ5A-Kv)b, eT?ĚK]kYTe]\lKٖ2:9C 'k2` Q> Mm!jF"-X_뺂X뱂~G&{3 xwJp}JueB1|XfLw0y9DZrkYxA`eoѮa;Ś.@ 9Af·]V$kPjO[߅-;ٗH$⯫ ?7h4\g.]uEͼ\ST*D"~L}ׅ/ xQ)ӦưU_8VJٝdzFxlX=Q)75EǪ=i?;9ؓ ;Aj1Uw*0;'hySScC4G뛢S9~R2CYIj9݊ik"=k6a*:dF=n*/[:[xG@֜4;)hش:(,$9":,2DEF@-"9$2sfΜANֹN9$ywas2rJ$#xTmZl+Al&rWfVG=a:\FG$heϊCEH j:I?֘{+1VETV#ŲHHUf$C{:}neWo e<;AE7~1epZN$m}BWsQ^lhHv']eTSUȡWWWOsGIzW,CiE0=E+lX fe݁٨YjGAAA&zduQbLT2A^P$^>#F9+:ܴ{aeK"z<75=چ{)666}; 8~+eR"f d-Gnnhj2l1t 21s#L*U@h꣍vlLPTlV\qm%ښ45bL_>pE^BWUWW}V7yՔXw ݯ@MNߤBo֎wf|tH# Xt~D+,Xk.2bw&ݢUUS׬` `,hllw.-ʱ{dC`Sccٿ7eNEEn:  *24z;d7°c-҄tۊӦah`AzDWZ,///@٫%[# x3 V;єܕ7ə hp 2ܜ̺n׷bs`08f2vL+]>Tl50fGNڃ" m;Ák4]2 a!i 7FeP苑`1UIw5\;vD*+CVO^nE~AW+2ܜ:\%7'V =9fOBOO"1H._5ʶٍl^t(AH ybi+j2OT6EPu<+U[JxSDp`dO7R8& B_nll|67wd|-[:}KMvQ/Cty"Fl /@ Y{VQaUaXBHXGZnYCmlp9Bc}Skտ\\Eoo.Rĝh7"u͍WO|Ew\1Au1&uK7db8$F6IϖSnժp P 2̣O7}u;(d~[Ѻ:|)GLĵDžB[K"(EJzD Á Ң(aqÁ &o1˭iTõ h4LH6X#'h]1(f-ʈ@* 9丮׏`E=#շ n9z%Cw75^J6m phvSkHyhZ'y㊜>n/OꚚvKȬ1Pqgf#GFEV^h&oochlluE=kk K8 uݬhe4Baۈ|ժVEF:YlMя vpTxx{]FOQ@*}m>D]sݢ\?V{P<4u۞nQ蠨#R$*gwzQh9cwh46t1?NީKvZ,eRy:{A2B HzOrEݞ+7ՁV]q<[ꛚwJ`X(EFe:D8Cʞ"ylYd߸_fҺ:b'>CSӈF_̕`߫ .}* Nr+duSxny~F!c*tAOpJ-']MkkznGJw,P(+Gs $uU:\q)RZu XI"{.]o2~ckkkO*ctx5Ŭ W'f@O#O :L\WG ^rNf?e=qQKK˦p0'KrP7x[(zñ\?qsѦY|-`%gx*E3WWUFĞpАH25Nu䳧Ƚ?9Lp, _L&ch1"۳[9fXǂa.[D"xX8 "2ի c|*"HI$64HyyyAAAAI$dzq'B0dRLLjZO>ֿ竭S%ٴAiO֗(Uk8hSSY9-x̄?93gN|uDngqgqgqgqgqgq?=.Y@%&p;7=@莒>yKvE[)K\2cs{{NkqyD" %R2|S{S= B)K؝cۏT.{M4"@+@ `D&Х޻HUU9p6sAqbj cXݖyPcb9Zyz{ G =V8wl#b8㌳{q@:ZߺD"P(`^JEEE~0 }gD~F{O: 0SћwRP]]=qi }涩,]>tg渾煊D"jjQP(4"KfTV:v`08=s]Lcƀ=6f*K}+|z>J{#D%@2dDn]?"Ju!EvlܘVG&yi{P֪Wv+paV|0hr~cDm3rxQ~.uRڊr TZr-"k(3Y߿D-- 7wbF'w"&noP9ȺsU'4/ ҙTu0">,ȁ (lZwrObc FU~!C)Z(*TFeXɕؘ?!V!+#RqFLÛ*@ɂ|M],z^b.kD Մ.@RAOInLgS g3;+[SPA`Wk 0E=~cq*]9>Ƨ>pd -k MQSKQY=ENQqV&J2AfA^ ܇r<$qJ`m(ޡʤHWr8Ũ,)93_@TbA܁r$9xoG8]ѳVK*]M[|WeIl*ľXW1O'3۫f<,r0mg୊$Ɛnc0[C[XUȺU3|FV΅d14.Nb狇NrMo3<TsE06!Nh^+IGwhc1"վ;=E6$Q90=ԁ%,~qA[TbLPgEM6uLʃ(~`nv<0+@^_*+=GX9h^364[*<{u>ۯ"ko7 zΖ粅-suӿSL :Ò:5Pa¯'t<~}zNu](Fϋ9wQ>YW¨݁QE/{pK|&NP(MXe~<_ŹbK?zpMT*IH%}ի}/'(/Š1<@P(D(?Lom=$I _\T9Pb=K_$(* lArO@;TP(ݍʃ|H-;NPBh4c:$kJ* e #ES!jvJlf|?4y||Tje(Q}  3jhNd}o7q+}vh l1*{9e|pX m˱C{?yG CXYZ54 rWJi )&zF QtI=я-ߨßhxw׈>I萹 q<#€^p5$TJ-DYߢIFYϛEr,VN~mTT|<3xXDvu5B||QnTU{.k02g"c#:e{k: wIq1;A> 腱TM۞F/ >U ZQ}2a wl <p+C2 v[YgEAV4 887SMEl:j^yѽctPq3'Rs @_lniYVK9.Ld>E/ +,299iO/a#:E+w!zlf@g%Q&b iSJUaGL@OCrQBdhԿ}D٣zkV&r$,IDAT#_ЈcC<5нrIJ9!Ϫzٷ&kYq[iX>=Yǵ=+T6֜2)Hoo }A.6np mmm{w[֭[77g$ G *z(? ðY)\>RKd\ׄY tI(]RCkw rkV$Jv\.Ҧ^Զ8 atN/L%=Oe|]y--(T9d,r7H !lWY,eqyUD2N8^XAmzաXd z<άEZ"Dyj璨NIzΑp^`DU4€S-T*#V~ 8+|oimmWS'nzQ.Hr^# 739@c<a4AggLВ+#(+%kPeoP VDgg|8vܗdӫ,ɺ;b͢.VMV%yF`ȍ(OC2k8{А|)@':6Rs<zYB",U::!%Ei5Ucz.6ǎ3 LL(UxލȿW9哠sQh]@JS|5ɣ̠6$2oTd“%%Ν=ժ5r)(cvyCrRџ@g1EF?Z_,k)uv`Rիܻ{zLHe>֔tP(Cej|" ak)`x ԚsA6^gUYeУE#5Y-b*X֫zI3.JE_A? rU-5tE3Fa>KWl֭[6Gc19UV#ȣyX{|G jUNoyE&ʇ-z ,hU 8دcjgT*ueXc2$Z<iLÔ\) U|1lOˎؚ(sDScLq1ԫDF5- OѶ}UݕQ{r(k v{Ζk-G.q]w1f`zzz.5Ƨ5q,m7Nּ0/ ➞3Qf[pϚZѲ#UyaiUu@~czv4b'XcB73fhزe7Q7lxGXuZJX۔b>;]{{g)L:@\No3?^G9FNj Q,U6;;;_r]wқv1m۶M8ab JDrxdmPq.PtIur|k~n׿9?ժf1:q]5p[_yNɶ<O\f̘xk79T0fE[תS*$ԽXSJO KK ~3ʾu;A6BhX^M_:qQnO11x _yVShT#Ns㿯G7/uԩSN:uԩSN:u}3HuO&$IENDB`httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resources/ApacheLogo.png000066400000000000000000001042471434266521000306350ustar00rootroot00000000000000PNG  IHDRpK6bKGD IDATxy|e;s[irtg_E"*n,(7pAŋ( ¦ BY-&m{ڜ$gqB.^|ygwΜg&dI&dI&dI&@{L2$R Mq|ԩk9'dI&ly`!i :}yƴ_3^&dI&f[5 \\ TMds--gl\`Ⱦӧr"dI&_Wi0Wi۬9̞Wq]w{;޺765?[؅kw5$L2?!?O=T蠃/B>.бsΙW01{_h)//oi=kZbr'He l`7SRgk U:M{I&dIIP3/_~^>gΜNڔ32tLO7ws-szxiM]G2@K.^qEz "ej?in*p3MuT:jQ/Z#/]KRwŽ$y8Z}M$L2$o&Yfȓ˗/xQRxb@[vcUzl6O_Y[{ի~ZxCkds~Ybű##Ox;Y}ENk:aœ97=Vd{ohR^KD8$L2?1톪+V8ndK?x~[Z() x_xS_rʴ6HUݶLz 73gμGynͿ9:0\B@6b]U,Z[ 4E\JNC!D*cX !BjcHIxUjۢU9\iXr(ZU[6=_91Xy:bz v!g-S>d/Hz 2n忩1Z78_h٫VZ1\58ޮ߈5MڰqAZ:.2ߑϝ;w

O1L?bIv]k$N{]W3$ 얩d޼y.[ZQ:J,X… +N؊~{LJJJiM$vaI"X JمL4QBl)%/e$ %LSN':3/93JF"xXB 9nuz0G,[C4˓nN.5vT2*"Diy.k<[zOutV)/xRAk^DlQ/n6|`tYO۞cĭMMGwu*}$G̙ŋ-~WD>pGsGC=5\#>, T & G"tKiip Gy]^Wd 1&if(!VDRh#)Qk3ͼ#I6jSN5CR4( |߇Aq#BX+RZ\IYzR>qg WkSE#.)+ÃsjP3{{ ~_v=8JyY@Ϭ^0_=y^D/aE@1Hh-=֘owuuMHYġc{Ibo8oٲeȯE^UG.X HGG7pPUyޫ̊*˘nS-% "A a3HDLR(4gWJHr3($q,3kJ(|BZPK4)'lJIDt MNMb1& ]of]U,+_oo <"qŠϗ"/~ س̗Ԟ>Iv ntT6rl{sa==n3ikJp˺]4h=nUOu͞=߿L`R@M]Aǜ9s sKl2sLoGy~R[[ƘFAP՗[*KrbSW6D(nC q1@x"_BD4g*Q=3/=/:wEq\u}jP!#yqp*Y?x0Wg3guo5(;-ze~Ӿ?u?: @k}. 1DvG[s><9ʅbm|4Rw'>QD"1 Ү%|r>rUe455jje***>&guկ~WtJ5cO}XHS,r"$ſC!!GAIhЭO&Id?ҀY |pp\ؚ@O25'9gUߪ]!זH6ro/_L@^E9pu5'6!wҲQ=+AZ}(B[sb4p[շ=E. 3Iq| CblknteOس(_aK-Em "zNmMʞFPns7Nn-yuk$^^7Π})p(UqַKṂ+Aͱ%L2' ?$Aa eq:Giv4H!OYLj-פy2kAFlh_M|Kv^C);߻toqٰ8o-i'c͕%.ae{lInm]6+1 v dVͬ+8mn\.u;u b;6~E+"cF_VЏ7@y1`CB {1ion?JZx[ı[ZuNG+GS|c-ԡgM-tvjwNnmm0Z`fȧűnO$}^ HD9][@9%m͉+kkO{:WFu[m Ƿ5|weOEL8*Mo=玏ܵfMwW`iԆ)Ï{z,^8Gx9|_ Éu{9mDBq66uңx =ReD_F?xSb" x.Y!Ig%Pًrzm1Ģpa#l4ܘkA\4dzreɩї,q*?8ɖ;`rbӡ,_u*Zb ȔH+5nh*Ks+#1'7ܜ}rU;,[jժ޶Dr/ pm͏ fZ: r1mڴm͉gD߉Pi7pp>ȋJ P Lv\4րD"QꝻc+O㩜\<րD"Q+X>pX B4Ɖzi";b`kW~gҊKy E,t]|tuTdr)[k+V=g e ScpXnf\% f]Vr@~f3ufn~3-,YBˠ?!gɓe8zoGl`(h#!ndJFj^$Z=Pc\1DVu E|\ IDAT. +sL%U ogrȴOtԚgI~tM3nx]CNwD XqkX::GaeyeU]sQ[cۍ8UPф&U]wA% {Nlrn'ըsw2KD;{{M3XAjǹG/Xe]]]O be53HM1yUD"@|Q9U#[ZZU9ngk v{ 7|Tadp=p#---bul?rx&lkk9eO&_=5mڴjܳnq'2a=38ưnuZG`%`3yZr'>uS Ѣ 6 /!rReRb߻YVg3H} x @ &dK(uIi?G]^~$)Cu63 Kx0$W:8?$Q\k G_ 筴|TeEVsAiGJ19X \Zy#k[Rk' K 2y..Yϩ~UBCŢ_6e1ٳ7:Y.7UlY~Z;w 6m}^tZ=|?𺵁Zƥsu?y{/v[vA+W\֜xb1L&N@-C8\ksg~=Ef0=iO-ߐKbMJqKi"g= dLm:rXǶjժm͉灱9͚6v;Θ0icc}DxS7lMΟ~{_Zd0ӏ=,;tDdY 5c), X6/!jPPp(QaDngڋJif6ͱ(c㖁B?&c^ @?@xX#qBDއS,Vր-bJv?I'*5 R @ $5kY ꌡZˋ0 QOý\ߛb@Z*IK"=\5%@mlCTP oGftk (z=o++QC> x+.'9o.71O dй5|xCyVIG++s_@<04m qS r3%DJ,]qv땑vMW+KR5p$?g<]'S.$ +jN89JXcĀ0>ÏT}r{Ɋ囗{M;c?(n lj#*q+{[ [q҇ \Z}>;F}W/:cOQwM쎓JwD"Eul?yߣ ^' ,YQWkllp8F ㆦ[k_k:0A*cΛ9s毗/_~ d??ﻰsCe%N%_qÔaC#KaH aU+9)JQ@Qn,+ b_RӢ*1?_=5SM/af2qxeQh+a,濢p C?mR*L!/>B76닚 X/*V~饸<@98(xx|5d~B]ͯ_\۟&s.݇CU9ΐSݡZrƶcwtL8hǸCbh7} m)VmG'|׈q=%S;َ~w΄svè|@GGǻN9ZUEYf)"yW ~kEGyRU<3Ȱe#=aB tkXY8X=pاB,V@|U@4救 :(ȕ%Rҷ@|x$7[ Kap\ݔ8eĥ5ϦHqj(1tKi& 0fwG@2yD raDJr|8 Q2[yx' U&(Ggk" GiH}nv9ǎ[U~ۣSew,i@SA%G刀L CdkR#nԈ`*6@9b#zNyRǥEc =͢Ä%JVd!|;Zda1 X%on Zp(1Sr54?I-,2 9CU[X~ZϽLC)!fPm#E{/ExN%-PuW-XnmP5 4wqKr@ZEeTmMG+kDbH橵?~gk#y%nEU$ܫLnhM> jDk#-ՙv?;3D?g9ko/ŖoU j6vA8)t ,}AEXFEn;/qR/7E%ɘmqb:Bu_E__߽=wBTub. /~I$"&Vܡk,^ChXe?b*ԆqՒ#Y iiF0xz?P xdQ[s~*k&sTxvA"e\ jt$^7u1W^?۽͞=Q"bbXo^\xŷ8$>crp*sr JN PPc :l6)%#X\S:訐|n`:8x8X m !.,G2l:uZ#/i ϧ2|eigWn*}U4M}}ҼΡKߐkxM$ ǿ!Go|aGQ8k[֫iiٴ8TYyƈz(!n>>=ezא?#'"vٲe2\U T֕|?kUd^x."jv(gO\ʇMY \ݏvJa0|/KjƌhL]o6ʈK T&*1vږQKIk/X/E;f iZCZW$- M<pӣS 2 R0Y)<+Q,yoBUl i2;Q\"t b8q"nN Aȉ0.F֍|'VDW0%~ T߉<{<&8tJ⺌ӈD `| +뇳|,ɛKH'PZKfRox~ܲڊVqҁaXgyv?M 0w߳.TOUD/?6yM^sDb>KB9j֦#7FD>S_oUXq{ȃ<跑x8}}}-G'DחmkN< =ÿ9s ,Y*  N-o| a!}jx͙ C>k.R{7geV5C4(Ie\X#ZTDȘaȨykFu: pd.Q'tZ7*莳XZAӝRԍTi3Brީs)QX#TF@=Y-P0>yrx]zd}>N / ( V n@LOf\BVK^<3dNpʨe==bm0MqU4' {3#i)QF|[N+[ʢIaQBfX J_l? K?1/\Y!;>~;Nbh{bE6lHFr~ 0N+ x`(y; d ݑ *ىe& UInP]u|vWw4G=W z~̈́,t?=ahoj]gon(S8{eO$P[g}:::>d+ =?ثh>,TvsTUϽɇ_z37]-{8((LChi)Eqn̏/3Wʱ"+a KckJp`Ia*dĊ'KfL*6}4t,3߇7ݟpmi>{`1o婮Vw#g8l,Qx(1z5$aSm5a*C)<?/FŐڻ8҄pKv^k Oj)(ngJ&F7}tkw`@x<v3#9RDH3I ?)ɹicLVܝTH"H/Jg嫕[p$rFOq<0?5{n 3g``|ouyyc&{#֮]aC.7gt\(sK(޹B+j>4 jnCm3jR*LUAb퉣HUTºp 6RYQn hv~άPPzbOvw5@FЯtp p1qFd@fuZOSdǧmr*x^4SlJ&|Ǚ檖{@>Ճ1)f͚%Kz/PՅ"?BkXN4}䴹y}mZsT3GK' q&8s$\hۂ̪tEYH>?j_@պ}k9%Yy2o„_v^`U(¥жP!pk h6#ε'Px7y / g*ON\ŋ##^,.+%VHtI7'_'ߙ!`%I/**KJ'),%>zI%)Ńz)Nĉ&?3tFq3V)(ߤnm|%P^Ho/d8ClC"bM貽LƇ \`Kk'4ŖfJ*p|I9m'^A2Bw"T9xj /P}qOr#^m$ӡ!|n7s2J_Ogŕ|PQ. 0L}2~h^'d@'sKJ|ȦfA&hHQl"KWi3cbgRb|b(Q5ᘽ*䣇M IDAT7ECS_\|j'b7YPx&~xѾO-$l3ݺ؟<@3 V4v޷~Us̾e_VR-vnL }İCKJKmo۸qE4Hc m䉆ƜDo8g^B&X=ڧd[ZZZzǀݞ5A,[,D:H%׽Ӄj֦ɤ烈~=ŧO^*w\0b f9W2g,/W㱅.Ff@E;\;%c-YMs*bH)?wzH<Ͽ&S-L@X{@f? _X~@CSP=+Q4LJ%Excfr߃+>8.u_R0-0&`N n(* 9%5(u\G-kunbڄUMk뾠nuןQGp x#S/*(`PrLcsc"Tw0EHrJ p,3:&Sfx0wܡf'IT 2舚\mm/=b"JMUl+٥7qpHn}StL(5%K^XU;Esu6l\]v=7nƯeF kcqaP3b n^̯fPwZB;H 'ﶔ0=.%y\Kk(~ |`5Kش5*MpO*\/RL?;ml$ro)]1.k2yO*=ieŅ2&\I,sꉘ lcTIFHSuy}dAj~_6`)2?r*CNEn^&^ib M[` KGWDru(*R180j"3(f\| 2jKڹ7KTc=Ǿwfϛ]Ϻez4译HLՌS9;|`UA~_8(](FhA:lgMUfR55As |L1m‰u?I*Tg7*GqEk}4z-Xlǀϋ$Ujl6ִX_:EO9q,}{ W~%n8r枏k!^[b }c9vFe|9e*;\|b~ױ5+)kg^0_2؄˗ sKUo{QC~Ų|/M{ \C\?=Y ~*Yc&SEb?{h8&+IeS-e۸$YI9D'@I oCLogy-lp* Pxd6]|86-yPXaW5|܆ 8ͬ4p='ZU}&ʿ7q;n:F'ۛ;x=$KZL/'rY)Ʃ#ʙ>5i^Mnn୥\  -E՞;_b+p@-mNkkT8PVEbq{ kU80Y&ȟ\#46623{{UZGێ<=EE"'o+IUpWQo3sUJ]Cs#d#X#QTm#3P9 ü+@TY|5"r"fpNJ W_=}~[{iyzlLWoJW$-Ĕo\ O(ޛ݄pOރ XΜ{z81V?''\\8: h SrfWN |WiLqVKSth5$9_"$PN(lիSRy((g'`%|g#uXC |[X$G*y_dK߱h#T)<}/$ʘ#(S)94Wzi|UgP/ {Eh\[7 <Vij}Coa* @3oH2Ҷ9Φ- .|jz{O>ܚoIޙ1  O\ 붵ab4)?BPqGNƊO`mhi7c`5 jL OUIم{hGpFJ)0IDvD7,sEF1ǍT)媲Q,˭ - u$KwCU>#Y^Up^w*hCOz5m{1,y\g99X1Ÿ8ݴ?mw Y/smo s |#9S(^璓xO!DF:Yη3J>SX,7;xE[7XҤ^l\RHpw-K a[ .]͵KL?{ُ2+\ȋ/oŕ;Slǣy:q6&; g0IW9qsK98E9p-W&J?ֈ6 YLCn.~[gP-I8ؗ{ eɴYK3 7/{˥*5da$':QpVr sZHj-]\e9&-U7uII ;ͽ3dBpRNNl]R4yL?.T˗_4X>(#IE%> vސR07x“Dh:n`*Oi?v#UJ&/ Ȃ?~(7%沽:TR9—8rLop*9uL|ZoѴ p+4?ϑ '8o޼ ލ qD++0aILuÀ=+{ t7l,~7M${u-6{,wR|gUoNޤ{{3f1q8|=%Y홝LN61mkå/5qK43BʼTvO[)^ohMWkpmoʍVfzĒJ G;yU;pg=):4m`n/uci񭚘ybftkSI]ĉNߎ˅s7rA.w~旀•]7k1ylW3k&(8{ѯVGN>$ i J4O]<ϻC)."v˖-k׮%L2m4&N=?]Aqʦ˙W^KYؤIe83eʔ^Ijw)hLnu@8{"jh/JUx>T5b-m*$H )?iM2تFUz 2֚&͠_5?(q=Ke5fWذvuD2 I hN@1^lWόp'g_ZZ8꣏x{z{8CTZ|hiA|yq5wȖP1 Z@onwݪjI '.^ۡPhG"}3.Pe˖;sf1>)ֶc'cJUZ[,"YVD2{_E5+?]_ cԐscybzE!/B/| ɄLxgC3L1ƸdF42Ƹ:8`֬ř&cLO} w^#"6̙3gޭ#ƒ%K||(`kTb"""D$/3hTݤ54'cء"kuD/%H+FHk)^UM&LYJU 6Rե"X,7s~ `yq͉DB`_."Xk1_֞ Tcb>mYjY3k֬1xSʼϺhѦ tck]ܰ۴3* NXy}4(+z䍆;="87 a0ze}SnK%yyy%.2Ɯ TcLLU[1߷'"sRc1#"28q־8NVD$e zkmC!t U|7B1dcʕ1lei ޱz'\-Y"h?( TUժkfe9OȂ]خqg:L@ՖŤό,\`~"#dD}5F~(xWF38d*!{2]g 4f*^Y\9t^GdCCCvmm/}|Sҹk̑vq4vp/hhNT;1{xR4twcW1fJU?(9}K6,z r) |sˆMH 9{ch!H"kDYW{JuQyC pUՠ%x|&7&~&\U3:NH:iY[ܜB}R;5b֓<\0wd_OmmmI+I#f8Ln@0 S⾿#QV'Z֘8Gx,3,G-p6`"U腮&c(n=5Q-־jQ:Á/{o kO%w9Cl&2]I'y;p0kxېNJ,bnZpbk ۼOwg -w^bZAo)ZupFcGש>FbUÁG䅫pB[aP;\F4];Ns707pJb^Z~%coE;;k!v5bk 5;]M$8x^*Hok(k"jueA% 4>F4/JnN9eމ ex { IDATmQr6+T\dz|2$R չXeB$Y>2AzyljV`]y*`H ߂ F-NwJVOm _~sqLGf/'8??=bha( k[}SX:U֚@ "dC>Z< 9ʼ8w;o,^ȞP)3 8-{e=pCPh_Uat]  }Χ:X$ȃQ\qvG8H+#A̯}c>'Hcmc< *X B_=&DYUf!|,?DB573tt_YSLX ilt,l|<0l "TTUU,Trxֹe:./+6j$Pik#;Ђ7!Ri1U~dYƏ2PT0t.ZNaKcY8)2x?~ jkk};Ģ<ȫ`-ȡ ]Hd>^SS,KLb,73$@sfpvxVezIrmqTR㌦@-tb }lJ<$X|V8R,Mx۹78ZBѣ1PFFUFGY(B/w"ཪͯf(F礁cuuDl^K:MrHU mxdk@`%pG]+W5ҒP< !ʡP>8#ȀN$gݏ~-]xZ{--hAKR@F41C&z}ph5*\;O@~GpT,vHe#f6Pxàg75WV[[uvߍka8 W"牓'?#Ff8mHЩ*GuGq.!C ,B*'p`2U6Yw}ֈT!3JZ9.}:vt!UhVd4u9qLaK(:ר.rY{W66>mS#Y4xW_}|xʄHUܺ%}E*e=:I XaIW( P* V-RK#-@5"D!=gf& ,;~Gc=?krP@w3E>%MMfx7_b*xGКpSG ],YW7&+ /h~U$~Ad{F@GHߡ 7@dC.1(ғL>(`0gGu%0Pa745^X]"c[Mf4Tգ%@́466_z,{ (+2|KdT0CCH_݊1K v vCXVD"?\T vbf Z f=ֺ6תGhQ`6p1L@a3ug3|vK"zK>r (}#HHV} N44@054H$dww#L 0]T˭J"SUt^y& 07u%wn.9|\@Dds6h4 Wwo c64G |K1AurpE_ d;QafQ5A-Kv)b, eT?ĚK]kYTe]\lKٖ2:9C 'k2` Q> Mm!jF"-X_뺂X뱂~G&{3 xwJp}JueB1|XfLw0y9DZrkYxA`eoѮa;Ś.@ 9Af·]V$kPjO[߅-;ٗH$⯫ ?7h4\g.]uEͼ\ST*D"~L}ׅ/ xQ)ӦưU_8VJٝdzFxlX=Q)75EǪ=i?;9ؓ ;Aj1Uw*0;'hySScC4G뛢S9~R2CYIj9݊ik"=k6a*:dF=n*/[:[xG@֜4;)hش:(,$9":,2DEF@-"9$2sfΜANֹN9$ywas2rJ$#xTmZl+Al&rWfVG=a:\FG$heϊCEH j:I?֘{+1VETV#ŲHHUf$C{:}neWo e<;AE7~1epZN$m}BWsQ^lhHv']eTSUȡWWWOsGIzW,CiE0=E+lX fe݁٨YjGAAA&zduQbLT2A^P$^>#F9+:ܴ{aeK"z<75=چ{)666}; 8~+eR"f d-Gnnhj2l1t 21s#L*U@h꣍vlLPTlV\qm%ښ45bL_>pE^BWUWW}V7yՔXw ݯ@MNߤBo֎wf|tH# Xt~D+,Xk.2bw&ݢUUS׬` `,hllw.-ʱ{dC`Sccٿ7eNEEn:  *24z;d7°c-҄tۊӦah`AzDWZ,///@٫%[# x3 V;єܕ7ə hp 2ܜ̺n׷bs`08f2vL+]>Tl50fGNڃ" m;Ák4]2 a!i 7FeP苑`1UIw5\;vD*+CVO^nE~AW+2ܜ:\%7'V =9fOBOO"1H._5ʶٍl^t(AH ybi+j2OT6EPu<+U[JxSDp`dO7R8& B_nll|67wd|-[:}KMvQ/Cty"Fl /@ Y{VQaUaXBHXGZnYCmlp9Bc}Skտ\\Eoo.Rĝh7"u͍WO|Ew\1Au1&uK7db8$F6IϖSnժp P 2̣O7}u;(d~[Ѻ:|)GLĵDžB[K"(EJzD Á Ң(aqÁ &o1˭iTõ h4LH6X#'h]1(f-ʈ@* 9丮׏`E=#շ n9z%Cw75^J6m phvSkHyhZ'y㊜>n/OꚚvKȬ1Pqgf#GFEV^h&oochlluE=kk K8 uݬhe4Baۈ|ժVEF:YlMя vpTxx{]FOQ@*}m>D]sݢ\?V{P<4u۞nQ蠨#R$*gwzQh9cwh46t1?NީKvZ,eRy:{A2B HzOrEݞ+7ՁV]q<[ꛚwJ`X(EFe:D8Cʞ"ylYd߸_fҺ:b'>CSӈF_̕`߫ .}* Nr+duSxny~F!c*tAOpJ-']MkkznGJw,P(+Gs $uU:\q)RZu XI"{.]o2~ckkkO*ctx5Ŭ W'f@O#O :L\WG ^rNf?e=qQKK˦p0'KrP7x[(zñ\?qsѦY|-`%gx*E3WWUFĞpАH25Nu䳧Ƚ?9Lp, _L&ch1"۳[9fXǂa.[D"xX8 "2ի c|*"HI$64HyyyAAAAI$dzq'B0dRLLjZO>ֿ竭S%ٴAiO֗(Uk8hSSY9-x̄?93gN|uDngqgqgqgqgqgq?=.Y@%&p;7=@莒>yKvE[)K\2cs{{NkqyD" %R2|S{S= B)K؝cۏT.{M4"@+@ `D&Х޻HUU9p6sAqbj cXݖyPcb9Zyz{ G =V8wl#b8㌳{q@:ZߺD"P(`^JEEE~0 }gD~F{O: 0SћwRP]]=qi }涩,]>tg渾煊D"jjQP(4"KfTV:v`08=s]Lcƀ=6f*K}+|z>J{#D%@2dDn]?"Ju!EvlܘVG&yi{P֪Wv+paV|0hr~cDm3rxQ~.uRڊr TZr-"k(3Y߿D-- 7wbF'w"&noP9ȺsU'4/ ҙTu0">,ȁ (lZwrObc FU~!C)Z(*TFeXɕؘ?!V!+#RqFLÛ*@ɂ|M],z^b.kD Մ.@RAOInLgS g3;+[SPA`Wk 0E=~cq*]9>Ƨ>pd -k MQSKQY=ENQqV&J2AfA^ ܇r<$qJ`m(ޡʤHWr8Ũ,)93_@TbA܁r$9xoG8]ѳVK*]M[|WeIl*ľXW1O'3۫f<,r0mg୊$Ɛnc0[C[XUȺU3|FV΅d14.Nb狇NrMo3<TsE06!Nh^+IGwhc1"վ;=E6$Q90=ԁ%,~qA[TbLPgEM6uLʃ(~`nv<0+@^_*+=GX9h^364[*<{u>ۯ"ko7 zΖ粅-suӿSL :Ò:5Pa¯'t<~}zNu](Fϋ9wQ>YW¨݁QE/{pK|&NP(MXe~<_ŹbK?zpMT*IH%}ի}/'(/Š1<@P(D(?Lom=$I _\T9Pb=K_$(* lArO@;TP(ݍʃ|H-;NPBh4c:$kJ* e #ES!jvJlf|?4y||Tje(Q}  3jhNd}o7q+}vh l1*{9e|pX m˱C{?yG CXYZ54 rWJi )&zF QtI=я-ߨßhxw׈>I萹 q<#€^p5$TJ-DYߢIFYϛEr,VN~mTT|<3xXDvu5B||QnTU{.k02g"c#:e{k: wIq1;A> 腱TM۞F/ >U ZQ}2a wl <p+C2 v[YgEAV4 887SMEl:j^yѽctPq3'Rs @_lniYVK9.Ld>E/ +,299iO/a#:E+w!zlf@g%Q&b iSJUaGL@OCrQBdhԿ}D٣zkV&r$,IDAT#_ЈcC<5нrIJ9!Ϫzٷ&kYq[iX>=Yǵ=+T6֜2)Hoo }A.6np mmm{w[֭[77g$ G *z(? ðY)\>RKd\ׄY tI(]RCkw rkV$Jv\.Ҧ^Զ8 atN/L%=Oe|]y--(T9d,r7H !lWY,eqyUD2N8^XAmzաXd z<άEZ"Dyj璨NIzΑp^`DU4€S-T*#V~ 8+|oimmWS'nzQ.Hr^# 739@c<a4AggLВ+#(+%kPeoP VDgg|8vܗdӫ,ɺ;b͢.VMV%yF`ȍ(OC2k8{А|)@':6Rs<zYB",U::!%Ei5Ucz.6ǎ3 LL(UxލȿW9哠sQh]@JS|5ɣ̠6$2oTd“%%Ν=ժ5r)(cvyCrRџ@g1EF?Z_,k)uv`Rիܻ{zLHe>֔tP(Cej|" ak)`x ԚsA6^gUYeУE#5Y-b*X֫zI3.JE_A? rU-5tE3Fa>KWl֭[6Gc19UV#ȣyX{|G jUNoyE&ʇ-z ,hU 8دcjgT*ueXc2$Z<iLÔ\) U|1lOˎؚ(sDScLq1ԫDF5- OѶ}UݕQ{r(k v{Ζk-G.q]w1f`zzz.5Ƨ5q,m7Nּ0/ ➞3Qf[pϚZѲ#UyaiUu@~czv4b'XcB73fhزe7Q7lxGXuZJX۔b>;]{{g)L:@\No3?^G9FNj Q,U6;;;_r]wқv1m۶M8ab JDrxdmPq.PtIur|k~n׿9?ժf1:q]5p[_yNɶ<O\f̘xk79T0fE[תS*$ԽXSJO KK ~3ʾu;A6BhX^M_:qQnO11x _yVShT#Ns㿯G7/uԩSN:uԩSN:u}3HuO&$IENDB`escapedHeader.httpbytes.serialized000066400000000000000000000005411434266521000346550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-Length: 11 hc-esc-hc-test-1: hc-test-1-value hc-esc-hc-sk: hc-sk-value hc-esc-hc-resp-date: hc-resp-date-value hc-esc-hc-req-date-date: hc-req-date-value hc-esc-hc-varmap-key: hc-varmap-key-value hc-esc-hc-varmap-val: hc-varmap-val-value hc-sk: xyzzy hc-resp-date: 2611108800000 hc-req-date: 165214800000 Hello WorldinvalidHeader.httpbytes.serialized000066400000000000000000000002441434266521000346770ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-type: text/html Cache-control: public, max-age=31536000 hc-sk: xyzzy hc-resp-date: badbadbad hc-req-date: 165214800000 Hello World httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resources/log4j2.xml000066400000000000000000000022101434266521000277330ustar00rootroot00000000000000 missingHeader.httpbytes.serialized000066400000000000000000000002321434266521000347170ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-type: text/html Cache-control: public, max-age=31536000 hc-resp-date: 2611108800000 hc-req-date: 165214800000 Hello World httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resources/noBody.httpbytes.serialized000066400000000000000000000002701434266521000334500ustar00rootroot00000000000000HTTP/1.1 204 No Content Content-type: text/html Cache-control: public, max-age=31536000 hc-sk: xyzzy hc-resp-date: 2611108800000 hc-req-date: 165214800000 hc-no-content: true simpleObject.httpbytes.serialized000066400000000000000000000002721434266521000345610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-type: text/html Cache-control: public, max-age=31536000 Content-Length: 11 hc-sk: xyzzy hc-resp-date: 2611108800000 hc-req-date: 165214800000 Hello WorldvariantMap.httpbytes.serialized000066400000000000000000000006261434266521000342460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-type: text/html Cache-control: public, max-age=31536000 Content-Length: 11 hc-sk: xyzzy hc-resp-date: 2611108800000 hc-req-date: 165214800000 hc-varmap-key: {Accept-Encoding=gzip} hc-varmap-val: {Accept-Encoding=gzip}https://example.com:1234/foo hc-varmap-key: {Accept-Encoding=compress} hc-varmap-val: {Accept-Encoding=compress}https://example.com:1234/foo Hello WorldvariantMapMissingKey.httpbytes.serialized000066400000000000000000000003571434266521000362520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-type: text/html Cache-control: public, max-age=31536000 hc-sk: xyzzy hc-resp-date: 2611108800000 hc-req-date: 165214800000 hc-varmap-val: {Accept-Encoding=compress}https://example.com:1234/foo Hello World variantMapMissingValue.httpbytes.serialized000066400000000000000000000003171434266521000365720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-cache/src/test/resourcesHTTP/1.1 200 OK Content-type: text/html Cache-control: public, max-age=31536000 hc-sk: xyzzy hc-resp-date: 2611108800000 hc-req-date: 165214800000 hc-varmap-key: {Accept-Encoding=gzip} Hello World httpcomponents-client-rel-v5.2.1/httpclient5-fluent/000077500000000000000000000000001434266521000225675ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/pom.xml000066400000000000000000000071651434266521000241150ustar00rootroot00000000000000 4.0.0 org.apache.httpcomponents.client5 httpclient5-parent 5.2.1 httpclient5-fluent Apache HttpClient Fluent 2011 Apache HttpComponents Client Fluent jar org.apache.httpcomponents.client5.httpclient5.fluent org.apache.httpcomponents.client5 httpclient5 org.apache.httpcomponents.client5 httpclient5 test tests org.slf4j slf4j-api org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.junit.jupiter junit-jupiter test org.hamcrest hamcrest test org.mockito mockito-core test maven-project-info-reports-plugin false index dependencies dependency-info summary httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/000077500000000000000000000000001434266521000233565ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/000077500000000000000000000000001434266521000243025ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/000077500000000000000000000000001434266521000252235ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/000077500000000000000000000000001434266521000260125ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/000077500000000000000000000000001434266521000272335ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/000077500000000000000000000000001434266521000276255ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/000077500000000000000000000000001434266521000311705ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000321475ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/000077500000000000000000000000001434266521000334445ustar00rootroot00000000000000Async.java000066400000000000000000000100741434266521000353070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.io.HttpClientResponseHandler; /** * Asynchronous executor for {@link Request}s. * * @since 4.3 */ public class Async { private Executor executor; private java.util.concurrent.Executor concurrentExec; public static Async newInstance() { return new Async(); } Async() { super(); } public Async use(final Executor executor) { this.executor = executor; return this; } public Async use(final java.util.concurrent.Executor concurrentExec) { this.concurrentExec = concurrentExec; return this; } static class ExecRunnable implements Runnable { private final BasicFuture future; private final Request request; private final Executor executor; private final HttpClientResponseHandler handler; ExecRunnable( final BasicFuture future, final Request request, final Executor executor, final HttpClientResponseHandler handler) { super(); this.future = future; this.request = request; this.executor = executor; this.handler = handler; } @Override public void run() { try { final Response response = this.executor.execute(this.request); final T result = response.handleResponse(this.handler); this.future.completed(result); } catch (final Exception ex) { this.future.failed(ex); } } } public Future execute( final Request request, final HttpClientResponseHandler handler, final FutureCallback callback) { final BasicFuture future = new BasicFuture<>(callback); final ExecRunnable runnable = new ExecRunnable<>( future, request, this.executor != null ? this.executor : Executor.newInstance(), handler); if (this.concurrentExec != null) { this.concurrentExec.execute(runnable); } else { final Thread t = new Thread(runnable); t.setDaemon(true); t.start(); } return future; } public Future execute(final Request request, final HttpClientResponseHandler handler) { return execute(request, handler, null); } public Future execute(final Request request, final FutureCallback callback) { return execute(request, new ContentResponseHandler(), callback); } public Future execute(final Request request) { return execute(request, new ContentResponseHandler(), null); } } Content.java000066400000000000000000000050521434266521000356440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; /** * This class represents arbitrary content of a specific type that can be consumed * multiple times and requires no explicit deallocation used by the fluent facade. * * @since 4.2 */ public class Content { public static final Content NO_CONTENT = new Content(new byte[] {}, ContentType.DEFAULT_BINARY); private final byte[] raw; private final ContentType type; public Content(final byte[] raw, final ContentType type) { super(); this.raw = raw; this.type = type; } public ContentType getType() { return this.type; } public byte[] asBytes() { return this.raw.clone(); } public String asString() { Charset charset = this.type.getCharset(); if (charset == null) { charset = StandardCharsets.ISO_8859_1; } return asString(charset); } /** * @since 4.4 */ public String asString(final Charset charset) { return new String(this.raw, charset); } public InputStream asStream() { return new ByteArrayInputStream(this.raw); } @Override public String toString() { return asString(); } } ContentResponseHandler.java000066400000000000000000000040371434266521000406630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.io.IOException; import org.apache.hc.client5.http.impl.classic.AbstractHttpClientResponseHandler; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; /** * {@link org.apache.hc.core5.http.io.HttpClientResponseHandler} implementation * that converts {@link org.apache.hc.core5.http.HttpResponse} messages * to {@link Content} instances. * * @see Content * * @since 4.4 */ public class ContentResponseHandler extends AbstractHttpClientResponseHandler { @Override public Content handleEntity(final HttpEntity entity) throws IOException { return entity != null ? new Content(EntityUtils.toByteArray(entity), ContentType.parse(entity.getContentType())) : Content.NO_CONTENT; } } Executor.java000066400000000000000000000217071434266521000360350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.io.IOException; import java.net.URISyntaxException; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsStore; import org.apache.hc.client5.http.auth.NTCredentials; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.auth.BasicAuthCache; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.TimeValue; /** * Executor for {@link Request}s. *

* A connection pool with maximum 100 connections per route and * a total maximum of 200 connections is used internally. * * @since 4.2 */ public class Executor { final static CloseableHttpClient CLIENT; static { CLIENT = HttpClientBuilder.create() .setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create() .useSystemProperties() .setMaxConnPerRoute(100) .setMaxConnTotal(200) .setDefaultConnectionConfig(ConnectionConfig.custom() .setValidateAfterInactivity(TimeValue.ofSeconds(10)) .build()) .build()) .useSystemProperties() .evictExpiredConnections() .evictIdleConnections(TimeValue.ofMinutes(1)) .build(); } public static Executor newInstance() { return new Executor(CLIENT); } public static Executor newInstance(final CloseableHttpClient httpclient) { return new Executor(httpclient != null ? httpclient : CLIENT); } private final CloseableHttpClient httpclient; private final AuthCache authCache; private volatile CredentialsStore credentialsStore; private volatile CookieStore cookieStore; Executor(final CloseableHttpClient httpclient) { super(); this.httpclient = httpclient; this.authCache = new BasicAuthCache(); } /** * @since 4.5 */ public Executor use(final CredentialsStore credentialsStore) { this.credentialsStore = credentialsStore; return this; } public Executor auth(final AuthScope authScope, final Credentials credentials) { CredentialsStore credentialsStoreSnapshot = credentialsStore; if (credentialsStoreSnapshot == null) { credentialsStoreSnapshot = new BasicCredentialsProvider(); this.credentialsStore = credentialsStoreSnapshot; } credentialsStoreSnapshot.setCredentials(authScope, credentials); return this; } public Executor auth(final HttpHost host, final Credentials credentials) { return auth(new AuthScope(host), credentials); } /** * @since 4.4 */ public Executor auth(final String host, final Credentials credentials) { final HttpHost httpHost; try { httpHost = HttpHost.create(host); } catch (final URISyntaxException ex) { throw new IllegalArgumentException("Invalid host: " + host); } return auth(httpHost, credentials); } public Executor authPreemptive(final HttpHost host) { final CredentialsStore credentialsStoreSnapshot = credentialsStore; if (credentialsStoreSnapshot != null) { final Credentials credentials = credentialsStoreSnapshot.getCredentials(new AuthScope(host), null); if (credentials != null) { final BasicScheme basicScheme = new BasicScheme(); basicScheme.initPreemptive(credentials); this.authCache.put(host, basicScheme); } } return this; } /** * @since 4.4 */ public Executor authPreemptive(final String host) { final HttpHost httpHost; try { httpHost = HttpHost.create(host); } catch (final URISyntaxException ex) { throw new IllegalArgumentException("Invalid host: " + host); } return authPreemptive(httpHost); } public Executor authPreemptiveProxy(final HttpHost proxy) { final CredentialsStore credentialsStoreSnapshot = credentialsStore; if (credentialsStoreSnapshot != null) { final Credentials credentials = credentialsStoreSnapshot.getCredentials(new AuthScope(proxy), null); if (credentials != null) { final BasicScheme basicScheme = new BasicScheme(); basicScheme.initPreemptive(credentials); this.authCache.put(proxy, basicScheme); } } return this; } /** * @since 4.4 */ public Executor authPreemptiveProxy(final String proxy) { final HttpHost httpHost; try { httpHost = HttpHost.create(proxy); } catch (final URISyntaxException ex) { throw new IllegalArgumentException("Invalid host: " + proxy); } return authPreemptiveProxy(httpHost); } public Executor auth(final HttpHost host, final String username, final char[] password) { return auth(host, new UsernamePasswordCredentials(username, password)); } public Executor auth(final HttpHost host, final String username, final char[] password, final String workstation, final String domain) { return auth(host, new NTCredentials(username, password, workstation, domain)); } public Executor clearAuth() { final CredentialsStore credentialsStoreSnapshot = credentialsStore; if (credentialsStoreSnapshot != null) { credentialsStoreSnapshot.clear(); } return this; } /** * @since 4.5 */ public Executor use(final CookieStore cookieStore) { this.cookieStore = cookieStore; return this; } public Executor clearCookies() { final CookieStore cookieStoreSnapshot = cookieStore; if (cookieStoreSnapshot != null) { cookieStoreSnapshot.clear(); } return this; } /** * Executes the request. Please Note that response content must be processed * or discarded using {@link Response#discardContent()}, otherwise the * connection used for the request might not be released to the pool. * * @see Response#handleResponse(org.apache.hc.core5.http.io.HttpClientResponseHandler) * @see Response#discardContent() */ public Response execute( final Request request) throws IOException { final HttpClientContext localContext = HttpClientContext.create(); final CredentialsStore credentialsStoreSnapshot = credentialsStore; if (credentialsStoreSnapshot != null) { localContext.setAttribute(HttpClientContext.CREDS_PROVIDER, credentialsStoreSnapshot); } if (this.authCache != null) { localContext.setAttribute(HttpClientContext.AUTH_CACHE, this.authCache); } final CookieStore cookieStoreSnapshot = cookieStore; if (cookieStoreSnapshot != null) { localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStoreSnapshot); } return new Response(request.internalExecute(this.httpclient, localContext)); } } Form.java000066400000000000000000000035671434266521000351460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; /** * HTTP form used by the fluent facade. * * @since 4.2 */ public class Form { private final List params; public static Form form() { return new Form(); } Form() { super(); this.params = new ArrayList<>(); } public Form add(final String name, final String value) { this.params.add(new BasicNameValuePair(name, value)); return this; } public List build() { return new ArrayList<>(this.params); } } HttpHeader.java000066400000000000000000000031451434266521000362630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; class HttpHeader { public static final String CONTENT_LENGTH = "Content-Length"; public static final String DATE = "Date"; public static final String CACHE_CONTROL = "Cache-Control"; public static final String CONTENT_TYPE = "Content-Type"; public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; } Request.java000066400000000000000000000332361434266521000356670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.FileEntity; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.net.WWWFormCodec; import org.apache.hc.core5.util.Timeout; /** * HTTP request used by the fluent facade. * * @since 4.2 */ public class Request { /** * @deprecated This attribute is no longer supported as a part of the public API. */ @Deprecated public static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; /** * @deprecated This attribute is no longer supported as a part of the public API. */ @Deprecated public static final Locale DATE_LOCALE = Locale.US; /** * @deprecated This attribute is no longer supported as a part of the public API. */ @Deprecated public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT"); private final ClassicHttpRequest request; private Boolean useExpectContinue; private Timeout connectTimeout; private Timeout responseTimeout; private HttpHost proxy; public static Request create(final Method method, final URI uri) { return new Request(new HttpUriRequestBase(method.name(), uri)); } public static Request create(final String methodName, final String uri) { return new Request(new HttpUriRequestBase(methodName, URI.create(uri))); } public static Request create(final String methodName, final URI uri) { return new Request(new HttpUriRequestBase(methodName, uri)); } public static Request get(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.GET, uri)); } public static Request get(final String uri) { return new Request(new BasicClassicHttpRequest(Method.GET, uri)); } public static Request head(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.HEAD, uri)); } public static Request head(final String uri) { return new Request(new BasicClassicHttpRequest(Method.HEAD, uri)); } public static Request post(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.POST, uri)); } public static Request post(final String uri) { return new Request(new BasicClassicHttpRequest(Method.POST, uri)); } public static Request patch(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.PATCH, uri)); } public static Request patch(final String uri) { return new Request(new BasicClassicHttpRequest(Method.PATCH, uri)); } public static Request put(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.PUT, uri)); } public static Request put(final String uri) { return new Request(new BasicClassicHttpRequest(Method.PUT, uri)); } public static Request trace(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.TRACE, uri)); } public static Request trace(final String uri) { return new Request(new BasicClassicHttpRequest(Method.TRACE, uri)); } public static Request delete(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.DELETE, uri)); } public static Request delete(final String uri) { return new Request(new BasicClassicHttpRequest(Method.DELETE, uri)); } public static Request options(final URI uri) { return new Request(new BasicClassicHttpRequest(Method.OPTIONS, uri)); } public static Request options(final String uri) { return new Request(new BasicClassicHttpRequest(Method.OPTIONS, uri)); } Request(final ClassicHttpRequest request) { super(); this.request = request; } @SuppressWarnings("deprecation") ClassicHttpResponse internalExecute( final CloseableHttpClient client, final HttpClientContext localContext) throws IOException { final RequestConfig.Builder builder; if (client instanceof Configurable) { builder = RequestConfig.copy(((Configurable) client).getConfig()); } else { builder = RequestConfig.custom(); } if (this.useExpectContinue != null) { builder.setExpectContinueEnabled(this.useExpectContinue.booleanValue()); } if (this.connectTimeout != null) { builder.setConnectTimeout(this.connectTimeout); } if (this.responseTimeout != null) { builder.setResponseTimeout(this.responseTimeout); } if (this.proxy != null) { builder.setProxy(this.proxy); } final RequestConfig config = builder.build(); localContext.setRequestConfig(config); return client.executeOpen(null, this.request, localContext); } public Response execute() throws IOException { return execute(Executor.CLIENT); } public Response execute(final CloseableHttpClient client) throws IOException { return new Response(internalExecute(client, HttpClientContext.create())); } //// HTTP header operations public Request addHeader(final Header header) { this.request.addHeader(header); return this; } /** * @since 4.3 */ public Request setHeader(final Header header) { this.request.setHeader(header); return this; } public Request addHeader(final String name, final String value) { this.request.addHeader(name, value); return this; } /** * @since 4.3 */ public Request setHeader(final String name, final String value) { this.request.setHeader(name, value); return this; } public Request removeHeader(final Header header) { this.request.removeHeader(header); return this; } public Request removeHeaders(final String name) { this.request.removeHeaders(name); return this; } public Request setHeaders(final Header... headers) { this.request.setHeaders(headers); return this; } public Request setCacheControl(final String cacheControl) { this.request.setHeader(HttpHeader.CACHE_CONTROL, cacheControl); return this; } ClassicHttpRequest getRequest() { return request; } /** * @deprecated Use {@link #setDate(Instant)} */ @Deprecated public Request setDate(final Date date) { this.request.setHeader(HttpHeader.DATE, DateUtils.formatStandardDate(DateUtils.toInstant(date))); return this; } /** * @deprecated Use {@link #setIfModifiedSince(Instant)} */ @Deprecated public Request setIfModifiedSince(final Date date) { this.request.setHeader(HttpHeader.IF_MODIFIED_SINCE, DateUtils.formatStandardDate(DateUtils.toInstant(date))); return this; } /** * @deprecated Use {@link #setIfUnmodifiedSince(Instant)} */ @Deprecated public Request setIfUnmodifiedSince(final Date date) { this.request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, DateUtils.formatStandardDate(DateUtils.toInstant(date))); return this; } public Request setDate(final Instant instant) { this.request.setHeader(HttpHeader.DATE, DateUtils.formatStandardDate(instant)); return this; } public Request setIfModifiedSince(final Instant instant) { this.request.setHeader(HttpHeader.IF_MODIFIED_SINCE, DateUtils.formatStandardDate(instant)); return this; } public Request setIfUnmodifiedSince(final Instant instant) { this.request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, DateUtils.formatStandardDate(instant)); return this; } //// HTTP protocol parameter operations public Request version(final HttpVersion version) { this.request.setVersion(version); return this; } public Request useExpectContinue() { this.useExpectContinue = Boolean.TRUE; return this; } public Request userAgent(final String agent) { this.request.setHeader(HttpHeaders.USER_AGENT, agent); return this; } //// HTTP connection parameter operations public Request connectTimeout(final Timeout timeout) { this.connectTimeout = timeout; return this; } public Request responseTimeout(final Timeout timeout) { this.responseTimeout = timeout; return this; } //// HTTP connection route operations public Request viaProxy(final HttpHost proxy) { this.proxy = proxy; return this; } /** * @since 4.4 */ public Request viaProxy(final String proxy) { try { this.proxy = HttpHost.create(proxy); } catch (final URISyntaxException e) { throw new IllegalArgumentException("Invalid host"); } return this; } //// HTTP entity operations public Request body(final HttpEntity entity) { this.request.setEntity(entity); return this; } public Request bodyForm(final Iterable formParams, final Charset charset) { final List paramList = new ArrayList<>(); for (final NameValuePair param : formParams) { paramList.add(param); } final ContentType contentType = charset != null ? ContentType.APPLICATION_FORM_URLENCODED.withCharset(charset) : ContentType.APPLICATION_FORM_URLENCODED; final String s = WWWFormCodec.format(paramList, contentType.getCharset()); return bodyString(s, contentType); } public Request bodyForm(final Iterable formParams) { return bodyForm(formParams, StandardCharsets.ISO_8859_1); } public Request bodyForm(final NameValuePair... formParams) { return bodyForm(Arrays.asList(formParams), StandardCharsets.ISO_8859_1); } public Request bodyString(final String s, final ContentType contentType) { final Charset charset = contentType != null ? contentType.getCharset() : null; final byte[] raw = charset != null ? s.getBytes(charset) : s.getBytes(); return body(new ByteArrayEntity(raw, contentType)); } public Request bodyFile(final File file, final ContentType contentType) { return body(new FileEntity(file, contentType)); } public Request bodyByteArray(final byte[] b) { return body(new ByteArrayEntity(b, null)); } /** * @since 4.4 */ public Request bodyByteArray(final byte[] b, final ContentType contentType) { return body(new ByteArrayEntity(b, contentType)); } public Request bodyByteArray(final byte[] b, final int off, final int len) { return body(new ByteArrayEntity(b, off, len, null)); } /** * @since 4.4 */ public Request bodyByteArray(final byte[] b, final int off, final int len, final ContentType contentType) { return body(new ByteArrayEntity(b, off, len, contentType)); } public Request bodyStream(final InputStream inStream) { return body(new InputStreamEntity(inStream, -1, null)); } public Request bodyStream(final InputStream inStream, final ContentType contentType) { return body(new InputStreamEntity(inStream, -1, contentType)); } @Override public String toString() { return this.request.toString(); } } Response.java000066400000000000000000000117011434266521000360260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.HttpResponseException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicResponseBuilder; /** * HTTP response used by the fluent facade. * * @since 4.2 */ public class Response { private final ClassicHttpResponse response; private boolean consumed; Response(final ClassicHttpResponse response) { super(); this.response = response; } private void assertNotConsumed() { if (this.consumed) { throw new IllegalStateException("Response content has been already consumed"); } } private void dispose() throws IOException { if (this.consumed) { return; } try { final HttpEntity entity = this.response.getEntity(); if (entity != null) { final InputStream content = entity.getContent(); if (content != null) { content.close(); } } } finally { this.consumed = true; this.response.close(); } } /** * Discards response content and deallocates all resources associated with it. */ public void discardContent() { try { dispose(); } catch (final Exception ignore) { } } /** * Handles the response using the specified {@link HttpClientResponseHandler} */ public T handleResponse(final HttpClientResponseHandler handler) throws IOException { assertNotConsumed(); try { return handler.handleResponse(this.response); } catch (final HttpException ex) { throw new ClientProtocolException(ex); } finally { dispose(); } } public Content returnContent() throws IOException { return handleResponse(new ContentResponseHandler()); } public HttpResponse returnResponse() throws IOException { assertNotConsumed(); try { final HttpEntity entity = this.response.getEntity(); return ClassicResponseBuilder.copy(response) .setEntity(entity != null ? new ByteArrayEntity( EntityUtils.toByteArray(entity), ContentType.parse(entity.getContentType())) : null) .build(); } finally { this.consumed = true; this.response.close(); } } public void saveContent(final File file) throws IOException { assertNotConsumed(); try { final int status = response.getCode(); if (status >= HttpStatus.SC_REDIRECTION) { throw new HttpResponseException(status, response.getReasonPhrase()); } try (FileOutputStream out = new FileOutputStream(file)) { final HttpEntity entity = this.response.getEntity(); if (entity != null) { entity.writeTo(out); } } } finally { this.consumed = true; this.response.close(); } } } package-info.java000066400000000000000000000024501434266521000365550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/main/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Simple facade APIs for HttpClient based on the concept of * a fluent interface. */ package org.apache.hc.client5.http.fluent; httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/000077500000000000000000000000001434266521000243355ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/000077500000000000000000000000001434266521000252565ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/000077500000000000000000000000001434266521000260455ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/000077500000000000000000000000001434266521000272665ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/000077500000000000000000000000001434266521000276605ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/000077500000000000000000000000001434266521000312235ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000322025ustar00rootroot00000000000000examples/000077500000000000000000000000001434266521000337415ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/httpfluent/000077500000000000000000000000001434266521000352365ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/examplesFluentAsync.java000066400000000000000000000065351434266521000403450ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/examples/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples.fluent; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.hc.client5.http.fluent.Async; import org.apache.hc.client5.http.fluent.Content; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.concurrent.FutureCallback; /** * This example demonstrates how the he HttpClient fluent API can be used to execute multiple * requests asynchronously using background threads. */ public class FluentAsync { public static void main(final String... args)throws Exception { // Use pool of two threads final ExecutorService threadpool = Executors.newFixedThreadPool(2); final Async async = Async.newInstance().use(threadpool); final Request[] requests = new Request[] { Request.get("http://www.google.com/"), Request.get("http://www.yahoo.com/"), Request.get("http://www.apache.org/"), Request.get("http://www.apple.com/") }; final Queue> queue = new LinkedList<>(); // Execute requests asynchronously for (final Request request: requests) { final Future future = async.execute(request, new FutureCallback() { @Override public void failed(final Exception ex) { System.out.println(ex.getMessage() + ": " + request); } @Override public void completed(final Content content) { System.out.println("Request completed: " + request); } @Override public void cancelled() { } }); queue.add(future); } while(!queue.isEmpty()) { final Future future = queue.remove(); try { future.get(); } catch (final ExecutionException ex) { } } System.out.println("Done"); threadpool.shutdown(); } } FluentExecutor.java000066400000000000000000000066621434266521000410670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/examples/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples.fluent; import java.io.File; import org.apache.hc.client5.http.fluent.Executor; import org.apache.hc.client5.http.fluent.Form; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how the he HttpClient fluent API can be used to execute multiple * requests within the same security context. The Executor class maintains a common context shared * by all requests executed with it. The Executor is thread-safe and can be used to execute * requests concurrently from multiple threads of execution. */ public class FluentExecutor { public static void main(final String... args)throws Exception { final Executor executor = Executor.newInstance() .auth(new HttpHost("somehost"), "username", "password".toCharArray()) .auth(new HttpHost("myproxy", 8080), "username", "password".toCharArray()) .authPreemptive(new HttpHost("myproxy", 8080)); // Execute a GET with timeout settings and return response content as String. executor.execute(Request.get("http://somehost/") .connectTimeout(Timeout.ofSeconds(1))) .returnContent().asString(); // Execute a POST with the 'expect-continue' handshake, using HTTP/1.1, // containing a request body as String and return response content as byte array. executor.execute(Request.post("http://somehost/do-stuff") .useExpectContinue() .version(HttpVersion.HTTP_1_1) .bodyString("Important stuff", ContentType.DEFAULT_TEXT) ).returnContent().asBytes(); // Execute a POST with a custom header through the proxy containing a request body // as an HTML form and save the result to the file executor.execute(Request.post("http://somehost/some-form") .addHeader("X-Custom-header", "stuff") .viaProxy(new HttpHost("myproxy", 8080)) .bodyForm(Form.form().add("username", "vip").add("password", "secret").build()) ).saveContent(new File("result.dump")); } } FluentRequests.java000066400000000000000000000055621434266521000411020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/examples/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples.fluent; import java.io.File; import org.apache.hc.client5.http.fluent.Form; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates basics of request execution with the HttpClient fluent API. */ public class FluentRequests { public static void main(final String... args)throws Exception { // Execute a GET with timeout settings and return response content as String. Request.get("http://somehost/") .connectTimeout(Timeout.ofSeconds(1)) .responseTimeout(Timeout.ofSeconds(5)) .execute().returnContent().asString(); // Execute a POST with the 'expect-continue' handshake, using HTTP/1.1, // containing a request body as String and return response content as byte array. Request.post("http://somehost/do-stuff") .useExpectContinue() .version(HttpVersion.HTTP_1_1) .bodyString("Important stuff", ContentType.DEFAULT_TEXT) .execute().returnContent().asBytes(); // Execute a POST with a custom header through the proxy containing a request body // as an HTML form and save the result to the file Request.post("http://somehost/some-form") .addHeader("X-Custom-header", "stuff") .viaProxy(new HttpHost("myproxy", 8080)) .bodyForm(Form.form().add("username", "vip").add("password", "secret").build()) .execute().saveContent(new File("result.dump")); } } FluentResponseHandling.java000066400000000000000000000074651434266521000425360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/examples/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples.fluent; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.HttpResponseException; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpStatus; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * This example demonstrates how the HttpClient fluent API can be used to handle HTTP responses * without buffering content body in memory. */ public class FluentResponseHandling { public static void main(final String... args)throws Exception { final Document result = Request.get("http://somehost/content") .execute().handleResponse(response -> { final int status = response.getCode(); final HttpEntity entity = response.getEntity(); if (status >= HttpStatus.SC_REDIRECTION) { throw new HttpResponseException(status, response.getReasonPhrase()); } if (entity == null) { throw new ClientProtocolException("Response contains no content"); } final DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); try { final DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); final ContentType contentType = ContentType.parseLenient(entity.getContentType()); if (!contentType.equals(ContentType.APPLICATION_XML)) { throw new ClientProtocolException("Unexpected content type:" + contentType); } Charset charset = contentType.getCharset(); if (charset == null) { charset = StandardCharsets.ISO_8859_1; } return docBuilder.parse(entity.getContent(), charset.name()); } catch (final ParserConfigurationException ex) { throw new IllegalStateException(ex); } catch (final SAXException ex) { throw new ClientProtocolException("Malformed XML document", ex); } }); // Do something useful with the result System.out.println(result); } } httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/fluent/000077500000000000000000000000001434266521000334775ustar00rootroot00000000000000TestRequest.java000066400000000000000000000062421434266521000365570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-fluent/src/test/java/org/apache/hc/client5/http/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.fluent; import java.lang.reflect.Method; import java.net.URI; import java.util.stream.Stream; import org.apache.hc.core5.http.ClassicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class TestRequest { private static final String URI_STRING_FIXTURE = "http://localhost"; private static final URI URI_FIXTURE = URI.create(URI_STRING_FIXTURE); public static Stream data() { return Stream.of( Arguments.of("delete", "DELETE"), Arguments.of("get", "GET"), Arguments.of("head", "HEAD"), Arguments.of("options", "OPTIONS"), Arguments.of("patch", "PATCH"), Arguments.of("post", "POST"), Arguments.of("put", "PUT"), Arguments.of("trace", "TRACE") ); } @ParameterizedTest(name = "{index}: {0} => {1}") @MethodSource("data") public void testCreateFromString(final String methodName, final String expectedMethod) throws Exception { final Method method = Request.class.getMethod(methodName, String.class); final Request request = (Request) method.invoke(null, URI_STRING_FIXTURE); final ClassicHttpRequest classicHttpRequest = request.getRequest(); Assertions.assertEquals(expectedMethod, classicHttpRequest.getMethod()); } @ParameterizedTest(name = "{index}: {0} => {1}") @MethodSource("data") public void testCreateFromURI(final String methodName, final String expectedMethod) throws Exception { final Method method = Request.class.getMethod(methodName, URI.class); final Request request = (Request) method.invoke(null, URI_FIXTURE); final ClassicHttpRequest classicHttpRequest = request.getRequest(); Assertions.assertEquals(expectedMethod, classicHttpRequest.getMethod()); } } httpcomponents-client-rel-v5.2.1/httpclient5-testing/000077500000000000000000000000001434266521000227475ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/000077500000000000000000000000001434266521000242165ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/.dockerignore000066400000000000000000000014271434266521000266760ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. */*/.svn httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/BUILDING.txt000066400000000000000000000017121434266521000261550ustar00rootroot00000000000000Building Docker containers for compatibility tests ======================================================== = Apache HTTPD 2.4 image Remark: omit sudo command if executing as root --- sudo docker build -t httpclient-tests-httpd apache-httpd --- = Squid 3.3 image Remark: omit sudo command if executing as root --- sudo docker build -t httpclient-tests-squid squid --- = Start containers --- sudo docker-compose up --- = SSL key / cert material (optional) # Issue a certificate request --- openssl req -config openssl.cnf -new -nodes -sha256 -days 36500 \ -subj '/O=Apache Software Foundation/OU=HttpComponents Project/CN=test-httpd/emailAddress=dev@hc.apache.org/' \ -keyout server-key.pem -out server-certreq.pem --- # Verify the request --- openssl req -in server-certreq.pem -text -noout --- # Sign new certificate with the test CA key --- openssl ca -config openssl.cnf -days 36500 -out server-cert.pem -in server-certreq.pem && rm server-certreq.pem --- httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/000077500000000000000000000000001434266521000265605ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/Dockerfile000066400000000000000000000033041434266521000305520ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM httpd:2.4 MAINTAINER dev@hc.apache.org ENV httpd_home /usr/local/apache2 ENV var_dir /var/httpd ENV www_dir ${var_dir}/www ENV private_dir ${www_dir}/private RUN apt-get update RUN apt-get install -y subversion RUN mkdir -p ${var_dir} RUN svn co --depth immediates http://svn.apache.org/repos/asf/httpcomponents/site ${www_dir} RUN svn up --set-depth infinity ${www_dir}/images RUN svn up --set-depth infinity ${www_dir}/css RUN mkdir ${httpd_home}/ssl COPY server-cert.pem ${httpd_home}/ssl/ COPY server-key.pem ${httpd_home}/ssl/ COPY httpd.conf ${httpd_home}/conf/ COPY httpd-ssl.conf ${httpd_home}/conf/extra/ RUN mkdir -p ${private_dir} # user: testuser; pwd: nopassword RUN echo "testuser:{SHA}0Ybo2sSKJNARW1aNCrLJ6Lguats=" > ${private_dir}/.htpasswd RUN echo "testuser:Restricted Files:73deccd22e07066db8c405e5364335f5" > ${private_dir}/.htpasswd_digest RUN echo "Big Secret" > ${private_dir}/big-secret.txt EXPOSE 8080 EXPOSE 8443 httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/httpd-ssl.conf000066400000000000000000000343011434266521000313520ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ========================================================================== # # This is the Apache server configuration file providing SSL support. # It contains the configuration directives to instruct the server how to # serve pages over an https connection. For detailed information about these # directives see # # Do NOT simply read the instructions in here without understanding # what they do. They're here only as hints or reminders. If you are unsure # consult the online docs. You have been warned. # # Required modules: mod_log_config, mod_setenvif, mod_ssl, # socache_shmcb_module (for default value of SSLSessionCache) LoadModule ssl_module modules/mod_ssl.so LoadModule socache_shmcb_module modules/mod_socache_shmcb.so # # Pseudo Random Number Generator (PRNG): # Configure one or more sources to seed the PRNG of the SSL library. # The seed data should be of good random quality. # WARNING! On some platforms /dev/random blocks if not enough entropy # is available. This means you then cannot use the /dev/random device # because it would lead to very long connection times (as long as # it requires to make more entropy available). But usually those # platforms additionally provide a /dev/urandom device which doesn't # block. So, if available, use this one instead. Read the mod_ssl User # Manual for more details. # #SSLRandomSeed startup file:/dev/random 512 #SSLRandomSeed startup file:/dev/urandom 512 #SSLRandomSeed connect file:/dev/random 512 #SSLRandomSeed connect file:/dev/urandom 512 # # When we also provide SSL we have to listen to the # standard HTTP port (see above) and to the HTTPS port # Listen 8443 ## ## SSL Global Context ## ## All SSL configuration in this context applies both to ## the main server and all SSL-enabled virtual hosts. ## # SSL Cipher Suite: # List the ciphers that the client is permitted to negotiate, # and that httpd will negotiate as the client of a proxied server. # See the OpenSSL documentation for a complete list of ciphers, and # ensure these follow appropriate best practices for this deployment. # httpd 2.2.30, 2.4.13 and later force-disable aNULL, eNULL and EXP ciphers, # while OpenSSL disabled these by default in 0.9.8zf/1.0.0r/1.0.1m/1.0.2a. SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES # By the end of 2016, only TLSv1.2 ciphers should remain in use. # Older ciphers should be disallowed as soon as possible, while the # kRSA ciphers do not offer forward secrecy. These changes inhibit # older clients (such as IE6 SP2 or IE8 on Windows XP, or other legacy # non-browser tooling) from successfully connecting. # # To restrict mod_ssl to use only TLSv1.2 ciphers, and disable # those protocols which do not support forward secrecy, replace # the SSLCipherSuite and SSLProxyCipherSuite directives above with # the following two directives, as soon as practical. # SSLCipherSuite HIGH:MEDIUM:!SSLv3:!kRSA # SSLProxyCipherSuite HIGH:MEDIUM:!SSLv3:!kRSA # User agents such as web browsers are not configured for the user's # own preference of either security or performance, therefore this # must be the prerogative of the web server administrator who manages # cpu load versus confidentiality, so enforce the server's cipher order. SSLHonorCipherOrder on # SSL Protocol support: # List the protocol versions which clients are allowed to connect with. # Disable SSLv3 by default (cf. RFC 7525 3.1.1). TLSv1 (1.0) should be # disabled as quickly as practical. By the end of 2016, only the TLSv1.2 # protocol or later should remain in use. SSLProtocol all -SSLv3 SSLProxyProtocol all -SSLv3 # Pass Phrase Dialog: # Configure the pass phrase gathering process. # The filtering dialog program (`builtin' is an internal # terminal dialog) has to provide the pass phrase on stdout. SSLPassPhraseDialog builtin # Inter-Process Session Cache: # Configure the SSL Session Cache: First the mechanism # to use and second the expiring timeout (in seconds). #SSLSessionCache "dbm:/usr/local/apache2/logs/ssl_scache" SSLSessionCache "shmcb:/usr/local/apache2/logs/ssl_scache(512000)" SSLSessionCacheTimeout 300 # OCSP Stapling (requires OpenSSL 0.9.8h or later) # # This feature is disabled by default and requires at least # the two directives SSLUseStapling and SSLStaplingCache. # Refer to the documentation on OCSP Stapling in the SSL/TLS # How-To for more information. # # Enable stapling for all SSL-enabled servers: #SSLUseStapling On # Define a relatively small cache for OCSP Stapling using # the same mechanism that is used for the SSL session cache # above. If stapling is used with more than a few certificates, # the size may need to be increased. (AH01929 will be logged.) #SSLStaplingCache "shmcb:/usr/local/apache2/logs/ssl_stapling(32768)" # Seconds before valid OCSP responses are expired from the cache #SSLStaplingStandardCacheTimeout 3600 # Seconds before invalid OCSP responses are expired from the cache #SSLStaplingErrorCacheTimeout 600 ## ## SSL Virtual Host Context ## # General setup for the virtual host Protocols h2 http/1.1 LogLevel http2:info H2Push on H2Direct on ServerAdmin dev@hc.apache.org ServerName localhost:8443 ErrorLog /proc/self/fd/2 TransferLog /proc/self/fd/2 DocumentRoot "/var/httpd/www" Options Indexes FollowSymLinks AllowOverride None Require all granted # SSL Engine Switch: # Enable/Disable SSL for this virtual host. SSLEngine on # Server Certificate: # Point SSLCertificateFile at a PEM encoded certificate. If # the certificate is encrypted, then you will be prompted for a # pass phrase. Note that a kill -HUP will prompt again. Keep # in mind that if you have both an RSA and a DSA certificate you # can configure both in parallel (to also allow the use of DSA # ciphers, etc.) # Some ECC cipher suites (http://www.ietf.org/rfc/rfc4492.txt) # require an ECC certificate which can also be configured in # parallel. SSLCertificateFile "/usr/local/apache2/ssl/server-cert.pem" #SSLCertificateFile "/usr/local/apache2/conf/server-dsa.crt" #SSLCertificateFile "/usr/local/apache2/conf/server-ecc.crt" # Server Private Key: # If the key is not combined with the certificate, use this # directive to point at the key file. Keep in mind that if # you've both a RSA and a DSA private key you can configure # both in parallel (to also allow the use of DSA ciphers, etc.) # ECC keys, when in use, can also be configured in parallel SSLCertificateKeyFile "/usr/local/apache2/ssl/server-key.pem" #SSLCertificateKeyFile "/usr/local/apache2/conf/server-dsa.key" #SSLCertificateKeyFile "/usr/local/apache2/conf/server-ecc.key" # Server Certificate Chain: # Point SSLCertificateChainFile at a file containing the # concatenation of PEM encoded CA certificates which form the # certificate chain for the server certificate. Alternatively # the referenced file can be the same as SSLCertificateFile # when the CA certificates are directly appended to the server # certificate for convenience. #SSLCertificateChainFile "/usr/local/apache2/conf/server-ca.crt" # Certificate Authority (CA): # Set the CA certificate verification path where to find CA # certificates for client authentication or alternatively one # huge file containing all of them (file must be PEM encoded) # Note: Inside SSLCACertificatePath you need hash symlinks # to point to the certificate files. Use the provided # Makefile to update the hash symlinks after changes. #SSLCACertificatePath "/usr/local/apache2/conf/ssl.crt" #SSLCACertificateFile "/usr/local/apache2/conf/ssl.crt/ca-bundle.crt" # Certificate Revocation Lists (CRL): # Set the CA revocation path where to find CA CRLs for client # authentication or alternatively one huge file containing all # of them (file must be PEM encoded). # The CRL checking mode needs to be configured explicitly # through SSLCARevocationCheck (defaults to "none" otherwise). # Note: Inside SSLCARevocationPath you need hash symlinks # to point to the certificate files. Use the provided # Makefile to update the hash symlinks after changes. #SSLCARevocationPath "/usr/local/apache2/conf/ssl.crl" #SSLCARevocationFile "/usr/local/apache2/conf/ssl.crl/ca-bundle.crl" #SSLCARevocationCheck chain # Client Authentication (Type): # Client certificate verification type and depth. Types are # none, optional, require and optional_no_ca. Depth is a # number which specifies how deeply to verify the certificate # issuer chain before deciding the certificate is not valid. #SSLVerifyClient require #SSLVerifyDepth 10 # TLS-SRP mutual authentication: # Enable TLS-SRP and set the path to the OpenSSL SRP verifier # file (containing login information for SRP user accounts). # Requires OpenSSL 1.0.1 or newer. See the mod_ssl FAQ for # detailed instructions on creating this file. Example: # "openssl srp -srpvfile /usr/local/apache2/conf/passwd.srpv -add username" #SSLSRPVerifierFile "/usr/local/apache2/conf/passwd.srpv" # Access Control: # With SSLRequire you can do per-directory access control based # on arbitrary complex boolean expressions containing server # variable checks and other lookup directives. The syntax is a # mixture between C and Perl. See the mod_ssl documentation # for more details. # #SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ # and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \ # and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \ # and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \ # and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \ # or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/ # # SSL Engine Options: # Set various options for the SSL engine. # o FakeBasicAuth: # Translate the client X.509 into a Basic Authorisation. This means that # the standard Auth/DBMAuth methods can be used for access control. The # user name is the `one line' version of the client's X.509 certificate. # Note that no password is obtained from the user. Every entry in the user # file needs this password: `xxj31ZMTZzkVA'. # o ExportCertData: # This exports two additional environment variables: SSL_CLIENT_CERT and # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the # server (always existing) and the client (only existing when client # authentication is used). This can be used to import the certificates # into CGI scripts. # o StdEnvVars: # This exports the standard SSL/TLS related `SSL_*' environment variables. # Per default this exportation is switched off for performance reasons, # because the extraction step is an expensive operation and is usually # useless for serving static content. So one usually enables the # exportation for CGI and SSI requests only. # o StrictRequire: # This denies access when "SSLRequireSSL" or "SSLRequire" applied even # under a "Satisfy any" situation, i.e. when it applies access is denied # and no other module can change it. # o OptRenegotiate: # This enables optimized SSL connection renegotiation handling when SSL # directives are used in per-directory context. #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire SSLOptions +StdEnvVars SSLOptions +StdEnvVars # SSL Protocol Adjustments: # The safe and default but still SSL/TLS standard compliant shutdown # approach is that mod_ssl sends the close notify alert but doesn't wait for # the close notify alert from client. When you need a different shutdown # approach you can use one of the following variables: # o ssl-unclean-shutdown: # This forces an unclean shutdown when the connection is closed, i.e. no # SSL close notify alert is sent or allowed to be received. This violates # the SSL/TLS standard but is needed for some brain-dead browsers. Use # this when you receive I/O errors because of the standard approach where # mod_ssl sends the close notify alert. # o ssl-accurate-shutdown: # This forces an accurate shutdown when the connection is closed, i.e. a # SSL close notify alert is send and mod_ssl waits for the close notify # alert of the client. This is 100% SSL/TLS standard compliant, but in # practice often causes hanging connections with brain-dead browsers. Use # this only for browsers where you know that their SSL implementation # works correctly. # Notice: Most problems of broken clients are also related to the HTTP # keep-alive facility, so you usually additionally want to disable # keep-alive for those clients, too. Use variable "nokeepalive" for this. # Similarly, one has to force some clients to use HTTP/1.0 to workaround # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and # "force-response-1.0" for this. BrowserMatch "MSIE [2-5]" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0 # Per-Server Logging: # The home of a custom SSL log file. Use this when you want a # compact non-error SSL logfile on a virtual host basis. CustomLog "/usr/local/apache2/logs/ssl_request_log" \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/httpd.conf000066400000000000000000000536441434266521000305660ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ========================================================================== # This is the main Apache HTTP server configuration file. It contains the # configuration directives that give the server its instructions. # See for detailed information. # In particular, see # # for a discussion of each configuration directive. # # Do NOT simply read the instructions in here without understanding # what they do. They're here only as hints or reminders. If you are unsure # consult the online docs. You have been warned. # # Configuration and logfile names: If the filenames you specify for many # of the server's control files begin with "/" (or "drive:/" for Win32), the # server will use that explicit path. If the filenames do *not* begin # with "/", the value of ServerRoot is prepended -- so "logs/access_log" # with ServerRoot set to "/usr/local/apache2" will be interpreted by the # server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log" # will be interpreted as '/logs/access_log'. # # ServerRoot: The top of the directory tree under which the server's # configuration, error, and log files are kept. # # Do not add a slash at the end of the directory path. If you point # ServerRoot at a non-local disk, be sure to specify a local disk on the # Mutex directive, if file-based mutexes are used. If you wish to share the # same ServerRoot for multiple httpd daemons, you will need to change at # least PidFile. # ServerRoot "/usr/local/apache2" # # Mutex: Allows you to set the mutex mechanism and mutex file directory # for individual mutexes, or change the global defaults # # Uncomment and change the directory if mutexes are file-based and the default # mutex file directory is not on a local disk or is not appropriate for some # other reason. # # Mutex default:logs # # Listen: Allows you to bind Apache to specific IP addresses and/or # ports, instead of the default. See also the # directive. # # Change this to Listen on specific IP addresses as shown below to # prevent Apache from glomming onto all bound IP addresses. # #Listen 12.34.56.78:80 Listen 8080 # # Dynamic Shared Object (DSO) Support # # To be able to use the functionality of a module which was built as a DSO you # have to place corresponding `LoadModule' lines at this location so the # directives contained in it are actually available _before_ they are used. # Statically compiled modules (those listed by `httpd -l') do not need # to be loaded here. # # Example: # LoadModule foo_module modules/mod_foo.so # LoadModule mpm_event_module modules/mod_mpm_event.so #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so #LoadModule mpm_worker_module modules/mod_mpm_worker.so LoadModule authn_file_module modules/mod_authn_file.so #LoadModule authn_dbm_module modules/mod_authn_dbm.so #LoadModule authn_anon_module modules/mod_authn_anon.so #LoadModule authn_dbd_module modules/mod_authn_dbd.so #LoadModule authn_socache_module modules/mod_authn_socache.so LoadModule authn_core_module modules/mod_authn_core.so LoadModule authz_host_module modules/mod_authz_host.so LoadModule authz_groupfile_module modules/mod_authz_groupfile.so LoadModule authz_user_module modules/mod_authz_user.so #LoadModule authz_dbm_module modules/mod_authz_dbm.so #LoadModule authz_owner_module modules/mod_authz_owner.so #LoadModule authz_dbd_module modules/mod_authz_dbd.so LoadModule authz_core_module modules/mod_authz_core.so #LoadModule authnz_ldap_module modules/mod_authnz_ldap.so #LoadModule authnz_fcgi_module modules/mod_authnz_fcgi.so LoadModule access_compat_module modules/mod_access_compat.so LoadModule auth_basic_module modules/mod_auth_basic.so #LoadModule auth_form_module modules/mod_auth_form.so LoadModule auth_digest_module modules/mod_auth_digest.so #LoadModule allowmethods_module modules/mod_allowmethods.so #LoadModule isapi_module modules/mod_isapi.so #LoadModule file_cache_module modules/mod_file_cache.so #LoadModule cache_module modules/mod_cache.so #LoadModule cache_disk_module modules/mod_cache_disk.so #LoadModule cache_socache_module modules/mod_cache_socache.so #LoadModule socache_shmcb_module modules/mod_socache_shmcb.so #LoadModule socache_dbm_module modules/mod_socache_dbm.so #LoadModule socache_memcache_module modules/mod_socache_memcache.so #LoadModule watchdog_module modules/mod_watchdog.so #LoadModule macro_module modules/mod_macro.so #LoadModule dbd_module modules/mod_dbd.so #LoadModule bucketeer_module modules/mod_bucketeer.so #LoadModule dumpio_module modules/mod_dumpio.so #LoadModule echo_module modules/mod_echo.so #LoadModule example_hooks_module modules/mod_example_hooks.so #LoadModule case_filter_module modules/mod_case_filter.so #LoadModule case_filter_in_module modules/mod_case_filter_in.so #LoadModule example_ipc_module modules/mod_example_ipc.so #LoadModule buffer_module modules/mod_buffer.so #LoadModule data_module modules/mod_data.so #LoadModule ratelimit_module modules/mod_ratelimit.so LoadModule reqtimeout_module modules/mod_reqtimeout.so #LoadModule ext_filter_module modules/mod_ext_filter.so #LoadModule request_module modules/mod_request.so #LoadModule include_module modules/mod_include.so LoadModule filter_module modules/mod_filter.so #LoadModule reflector_module modules/mod_reflector.so #LoadModule substitute_module modules/mod_substitute.so #LoadModule sed_module modules/mod_sed.so #LoadModule charset_lite_module modules/mod_charset_lite.so #LoadModule deflate_module modules/mod_deflate.so #LoadModule xml2enc_module modules/mod_xml2enc.so #LoadModule proxy_html_module modules/mod_proxy_html.so LoadModule mime_module modules/mod_mime.so #LoadModule ldap_module modules/mod_ldap.so LoadModule log_config_module modules/mod_log_config.so #LoadModule log_debug_module modules/mod_log_debug.so #LoadModule log_forensic_module modules/mod_log_forensic.so #LoadModule logio_module modules/mod_logio.so #LoadModule lua_module modules/mod_lua.so LoadModule env_module modules/mod_env.so #LoadModule mime_magic_module modules/mod_mime_magic.so #LoadModule cern_meta_module modules/mod_cern_meta.so #LoadModule expires_module modules/mod_expires.so LoadModule headers_module modules/mod_headers.so #LoadModule ident_module modules/mod_ident.so #LoadModule usertrack_module modules/mod_usertrack.so #LoadModule unique_id_module modules/mod_unique_id.so LoadModule setenvif_module modules/mod_setenvif.so LoadModule version_module modules/mod_version.so #LoadModule remoteip_module modules/mod_remoteip.so #LoadModule proxy_module modules/mod_proxy.so #LoadModule proxy_connect_module modules/mod_proxy_connect.so #LoadModule proxy_ftp_module modules/mod_proxy_ftp.so #LoadModule proxy_http_module modules/mod_proxy_http.so #LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so #LoadModule proxy_scgi_module modules/mod_proxy_scgi.so #LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so #LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so #LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so #LoadModule proxy_ajp_module modules/mod_proxy_ajp.so #LoadModule proxy_balancer_module modules/mod_proxy_balancer.so #LoadModule proxy_express_module modules/mod_proxy_express.so #LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so #LoadModule session_module modules/mod_session.so #LoadModule session_cookie_module modules/mod_session_cookie.so #LoadModule session_crypto_module modules/mod_session_crypto.so #LoadModule session_dbd_module modules/mod_session_dbd.so #LoadModule slotmem_shm_module modules/mod_slotmem_shm.so #LoadModule slotmem_plain_module modules/mod_slotmem_plain.so #LoadModule ssl_module modules/mod_ssl.so #LoadModule optional_hook_export_module modules/mod_optional_hook_export.so #LoadModule optional_hook_import_module modules/mod_optional_hook_import.so #LoadModule optional_fn_import_module modules/mod_optional_fn_import.so #LoadModule optional_fn_export_module modules/mod_optional_fn_export.so #LoadModule dialup_module modules/mod_dialup.so LoadModule http2_module modules/mod_http2.so #LoadModule proxy_http2_module modules/mod_proxy_http2.so #LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so #LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so #LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so #LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so LoadModule unixd_module modules/mod_unixd.so #LoadModule heartbeat_module modules/mod_heartbeat.so #LoadModule heartmonitor_module modules/mod_heartmonitor.so #LoadModule dav_module modules/mod_dav.so LoadModule status_module modules/mod_status.so LoadModule autoindex_module modules/mod_autoindex.so #LoadModule asis_module modules/mod_asis.so #LoadModule info_module modules/mod_info.so #LoadModule suexec_module modules/mod_suexec.so #LoadModule cgid_module modules/mod_cgid.so #LoadModule cgi_module modules/mod_cgi.so #LoadModule dav_fs_module modules/mod_dav_fs.so #LoadModule dav_lock_module modules/mod_dav_lock.so #LoadModule vhost_alias_module modules/mod_vhost_alias.so #LoadModule negotiation_module modules/mod_negotiation.so LoadModule dir_module modules/mod_dir.so #LoadModule imagemap_module modules/mod_imagemap.so #LoadModule actions_module modules/mod_actions.so #LoadModule speling_module modules/mod_speling.so #LoadModule userdir_module modules/mod_userdir.so LoadModule alias_module modules/mod_alias.so #LoadModule rewrite_module modules/mod_rewrite.so # # If you wish httpd to run as a different user or group, you must run # httpd as root initially and it will switch. # # User/Group: The name (or #number) of the user/group to run httpd as. # It is usually good practice to create a dedicated user and group for # running httpd, as with most system services. # User daemon Group daemon # 'Main' server configuration # # The directives in this section set up the values used by the 'main' # server, which responds to any requests that aren't handled by a # definition. These values also provide defaults for # any containers you may define later in the file. # # All of these directives may appear inside containers, # in which case these default settings will be overridden for the # virtual host being defined. # # # ServerAdmin: Your address, where problems with the server should be # e-mailed. This address appears on some server-generated pages, such # as error documents. e.g. admin@your-domain.com # ServerAdmin dev@hc.apache.org # # ServerName gives the name and port that the server uses to identify itself. # This can often be determined automatically, but we recommend you specify # it explicitly to prevent problems during startup. # # If your host doesn't have a registered DNS name, enter its IP address here. # ServerName localhost:8080 Protocols h2c http/1.1 LogLevel http2:info H2Push on # # Deny access to the entirety of your server's filesystem. You must # explicitly permit access to web content directories in other # blocks below. # AllowOverride none Require all denied # # Note that from this point forward you must specifically allow # particular features to be enabled - so if something's not working as # you might expect, make sure that you have specifically enabled it # below. # # # DocumentRoot: The directory out of which you will serve your # documents. By default, all requests are taken from this directory, but # symbolic links and aliases may be used to point to other locations. # DocumentRoot "/var/httpd/www" # # Possible values for the Options directive are "None", "All", # or any combination of: # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews # # Note that "MultiViews" must be named *explicitly* --- "Options All" # doesn't give it to you. # # The Options directive is both complicated and important. Please see # http://httpd.apache.org/docs/2.4/mod/core.html#options # for more information. # Options Indexes FollowSymLinks # # AllowOverride controls what directives may be placed in .htaccess files. # It can be "All", "None", or any combination of the keywords: # AllowOverride FileInfo AuthConfig Limit # AllowOverride None # # Controls who can get stuff from this server. # Require all granted Header add Link ";rel=preload" Header add Link ";rel=preload" Header add Link ";rel=preload" Header add Link ";rel=preload" Header add Link ";rel=preload" # # DirectoryIndex: sets the file that Apache will serve if a directory # is requested. # DirectoryIndex index.html # # The following lines prevent .htaccess and .htpasswd files from being # viewed by Web clients. # Require all denied # # ErrorLog: The location of the error log file. # If you do not specify an ErrorLog directive within a # container, error messages relating to that virtual host will be # logged here. If you *do* define an error logfile for a # container, that host's errors will be logged there and not here. # ErrorLog /proc/self/fd/2 # # LogLevel: Control the number of messages logged to the error_log. # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. # LogLevel warn # # The following directives define some format nicknames for use with # a CustomLog directive (see below). # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%h %l %u %t \"%r\" %>s %b" common # You need to enable mod_logio.c to use %I and %O LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio # # The location and format of the access logfile (Common Logfile Format). # If you do not define any access logfiles within a # container, they will be logged here. Contrariwise, if you *do* # define per- access logfiles, transactions will be # logged therein and *not* in this file. # CustomLog /proc/self/fd/1 common # # If you prefer a logfile with access, agent, and referer information # (Combined Logfile Format) you can use the following directive. # #CustomLog "logs/access_log" combined # # Redirect: Allows you to tell clients about documents that used to # exist in your server's namespace, but do not anymore. The client # will make a new request for the document at its new location. # Example: # Redirect permanent /foo http://www.example.com/bar # # Alias: Maps web paths into filesystem paths and is used to # access content that does not live under the DocumentRoot. # Example: # Alias /webpath /full/filesystem/path # # If you include a trailing / on /webpath then the server will # require it to be present in the URL. You will also likely # need to provide a section to allow access to # the filesystem path. # # ScriptAlias: This controls which directories contain server scripts. # ScriptAliases are essentially the same as Aliases, except that # documents in the target directory are treated as applications and # run by the server when requested rather than as documents sent to the # client. The same rules about trailing "/" apply to ScriptAlias # directives as to Alias. # ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/" # # ScriptSock: On threaded servers, designate the path to the UNIX # socket used to communicate with the CGI daemon of mod_cgid. # #Scriptsock cgisock # # "/usr/local/apache2/cgi-bin" should be changed to whatever your ScriptAliased # CGI directory exists, if you have that configured. # AllowOverride None Options None Require all granted # # Avoid passing HTTP_PROXY environment to CGI's on this or any proxied # backend servers which have lingering "httpoxy" defects. # 'Proxy' request header is undefined by the IETF, not listed by IANA # RequestHeader unset Proxy early # # TypesConfig points to the file containing the list of mappings from # filename extension to MIME-type. # TypesConfig conf/mime.types # # AddType allows you to add to or override the MIME configuration # file specified in TypesConfig for specific file types. # #AddType application/x-gzip .tgz # # AddEncoding allows you to have certain browsers uncompress # information on the fly. Note: Not all browsers support this. # #AddEncoding x-compress .Z #AddEncoding x-gzip .gz .tgz # # If the AddEncoding directives above are commented-out, then you # probably should define those extensions to indicate media types: # AddType application/x-compress .Z AddType application/x-gzip .gz .tgz # # AddHandler allows you to map certain file extensions to "handlers": # actions unrelated to filetype. These can be either built into the server # or added with the Action directive (see below) # # To use CGI scripts outside of ScriptAliased directories: # (You will also need to add "ExecCGI" to the "Options" directive.) # #AddHandler cgi-script .cgi # For type maps (negotiated resources): #AddHandler type-map var # # Filters allow you to process content before it is sent to the client. # # To parse .shtml files for server-side includes (SSI): # (You will also need to add "Includes" to the "Options" directive.) # #AddType text/html .shtml #AddOutputFilter INCLUDES .shtml # # The mod_mime_magic module allows the server to use various hints from the # contents of the file itself to determine its type. The MIMEMagicFile # directive tells the module where the hint definitions are located. # #MIMEMagicFile conf/magic # # Customizable error responses come in three flavors: # 1) plain text 2) local redirects 3) external redirects # # Some examples: #ErrorDocument 500 "The server made a boo boo." #ErrorDocument 404 /missing.html #ErrorDocument 404 "/cgi-bin/missing_handler.pl" #ErrorDocument 402 http://www.example.com/subscription_info.html # # # MaxRanges: Maximum number of Ranges in a request before # returning the entire resource, or one of the special # values 'default', 'none' or 'unlimited'. # Default setting is to accept 200 Ranges. #MaxRanges unlimited # # EnableMMAP and EnableSendfile: On systems that support it, # memory-mapping or the sendfile syscall may be used to deliver # files. This usually improves server performance, but must # be turned off when serving from networked-mounted # filesystems or if support for these functions is otherwise # broken on your system. # Defaults: EnableMMAP On, EnableSendfile Off # #EnableMMAP off #EnableSendfile on # Supplemental configuration # # The configuration files in the conf/extra/ directory can be # included to add extra features or to modify the default configuration of # the server, or you may simply copy their contents here and change as # necessary. # Server-pool management (MPM specific) #Include conf/extra/httpd-mpm.conf # Multi-language error messages #Include conf/extra/httpd-multilang-errordoc.conf # Fancy directory listings #Include conf/extra/httpd-autoindex.conf # Language settings #Include conf/extra/httpd-languages.conf # User home directories #Include conf/extra/httpd-userdir.conf # Real-time info on requests and configuration #Include conf/extra/httpd-info.conf # Virtual hosts #Include conf/extra/httpd-vhosts.conf # Local access to the Apache HTTP Server Manual #Include conf/extra/httpd-manual.conf # Distributed authoring and versioning (WebDAV) #Include conf/extra/httpd-dav.conf # Various default settings #Include conf/extra/httpd-default.conf # Configure mod_proxy_html to understand HTML4/XHTML1 Include conf/extra/proxy-html.conf # Secure (SSL/TLS) connections Include conf/extra/httpd-ssl.conf # # Note: The following must must be present to support # starting without SSL on platforms with no /dev/random equivalent # but a statically compiled-in mod_ssl. # SSLRandomSeed startup builtin SSLRandomSeed connect builtin AuthType Basic AuthName "Restricted Files" AuthBasicProvider file AuthUserFile "/var/httpd/www/private/.htpasswd" Require valid-user AuthType Digest AuthName "Restricted Files" AuthDigestDomain / AuthBasicProvider file AuthUserFile "/var/httpd/www/private/.htpasswd_digest" Require valid-user httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/index.txt000066400000000000000000000002031434266521000304230ustar00rootroot00000000000000V 21161223094143Z 01 unknown /O=Apache Software Foundation/OU=HttpComponents Project/CN=test-httpd/emailAddress=dev@hc.apache.org httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/openssl.cnf000066400000000000000000000257171434266521000307470ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # # This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = $ENV::HOME/.rnd # Extra OBJECT IDENTIFIER info: #oid_file = $ENV::HOME/.oid oid_section = new_oids # To use this configuration file with the "-extfile" option of the # "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: # extensions = # (Alternatively, use a configuration file that has only # X.509v3 extensions in its main [= default] section.) [ new_oids ] # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 # Policies used by the TSA examples. tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7 #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] dir = . # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several ctificates with same subject. new_certs_dir = $dir # default place for new certs. certificate = ../../../test-CA/ca-cert.pem # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = ../../../test-CA/ca-key.pem # The private key RANDFILE = ../../../test-CA/.rand # private random number file x509_extensions = usr_cert # The extentions to add to the cert # Comment out the following two lines for the "traditional" # (and highly broken) format. name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. copy_extensions = copy # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # crlnumber must also be commented out to leave a V1 CRL. # crl_extensions = crl_ext default_days = 365 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = default # use public key default MD preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy [ policy_match ] organizationName = match organizationalUnitName = match commonName = supplied emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 2048 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extentions to add to the self signed cert # Passwords for private keys if not present they will be prompted for # input_password = secret # output_password = secret # This sets a mask for permitted string types. There are several options. # default: PrintableString, T61String, BMPString. # pkix : PrintableString, BMPString (PKIX recommendation before 2004) # utf8only: only UTF8Strings (PKIX recommendation after 2004). # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). # MASK:XXXX a literal mask value. # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. string_mask = utf8only req_extensions = v3_req # The extensions to add to a certificate request [ req_distinguished_name ] 0.organizationName = Organization Name (eg, company) 0.organizationName_default = Apache Software Foundation organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = HttpComponents Project commonName = Common Name (e.g. server FQDN or YOUR name) commonName_max = 64 commonName_default = test-httpd emailAddress = Email Address emailAddress_max = 64 emailAddress_default = dev@hc.apache.org # SET-ex3 = SET extension number 3 [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name [ usr_cert ] # These extensions are added when 'ca' signs a request. # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This is required for TSA certificates. # extendedKeyUsage = critical,timeStamping [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [ alt_names ] DNS.1 = test-httpd DNS.2 = localhost [ v3_ca ] # Extensions for a typical CA # PKIX recommendation. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer # This is what PKIX recommends but some broken software chokes on critical # extensions. #basicConstraints = critical,CA:true # So we do this instead. basicConstraints = CA:true # Key usage: this is typical for a CA certificate. However since it will # prevent it being used as an test self-signed certificate it is best # left out by default. # keyUsage = cRLSign, keyCertSign # Some might want this also # nsCertType = sslCA, emailCA # Include email address in subject alt name: another PKIX recommendation # subjectAltName=email:copy # Copy issuer details # issuerAltName=issuer:copy # DER hex encoding of an extension: beware experts only! # obj=DER:02:03 # Where 'obj' is a standard or added object # You can even override a supported extension: # basicConstraints= critical, DER:30:03:01:01:FF [ crl_ext ] # CRL extensions. # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy authorityKeyIdentifier=keyid:always [ proxy_cert_ext ] # These extensions should be added when creating a proxy certificate # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo #################################################################### [ tsa ] default_tsa = tsa_config1 # the default TSA section [ tsa_config1 ] # These are used by the TSA reply generation only. dir = ./demoCA # TSA root directory serial = $dir/tsaserial # The current serial number (mandatory) crypto_device = builtin # OpenSSL engine to use for signing signer_cert = $dir/tsacert.pem # The TSA signing certificate # (optional) certs = $dir/cacert.pem # Certificate chain to include in reply # (optional) signer_key = $dir/private/tsakey.pem # The TSA private key (optional) default_policy = tsa_policy1 # Policy if request did not specify it # (optional) other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) digests = md5, sha1 # Acceptable message digests (mandatory) accuracy = secs:1, millisecs:500, microsecs:100 # (optional) clock_precision_digits = 0 # number of digits after dot. (optional) ordering = yes # Is ordering defined for timestamps? # (optional, default: no) tsa_name = yes # Must the TSA name be included in the reply? # (optional, default: no) ess_cert_id_chain = no # Must the ESS cert id chain be included? # (optional, default: no) httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/serial000066400000000000000000000000031434266521000277530ustar00rootroot0000000000000002 httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/server-cert.pem000066400000000000000000000114201434266521000315220ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: O=Apache Software Foundation, OU=HttpComponents Project, CN=Test CA/emailAddress=dev@hc.apache.org Validity Not Before: Jan 16 09:41:43 2017 GMT Not After : Dec 23 09:41:43 2116 GMT Subject: O=Apache Software Foundation, OU=HttpComponents Project, CN=test-httpd/emailAddress=dev@hc.apache.org Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:da:55:cb:73:c3:42:cf:c1:4e:6e:d9:74:b8:f8: 1c:3f:1a:de:8d:72:3a:c4:62:f7:eb:e4:72:5b:9b: 9e:65:09:0e:f4:9b:f0:bd:29:d5:af:a9:d1:5f:82: 99:53:49:1f:7a:5c:6f:6c:0f:a2:48:68:c7:53:3e: 9b:9f:b2:c2:eb:8f:6b:38:c4:6a:75:52:55:60:9d: 60:40:9b:a4:79:c6:c7:ae:1c:6c:d9:c8:b6:5b:cb: d4:af:78:45:0e:57:62:04:48:1d:d2:f3:c1:98:ac: 64:1f:ae:8d:30:78:ec:52:b3:03:6c:4b:1c:b1:87: 56:5e:a4:c3:3c:54:6b:05:22:95:30:c8:0c:d4:d4: 43:f0:eb:5b:58:29:5c:ce:98:97:cc:86:7a:8a:fd: 70:0e:c0:55:57:21:2e:4a:f5:5d:be:ba:6e:76:99: 6a:c7:9d:9f:5f:31:63:9c:ae:b5:03:75:6c:ec:d7: e8:75:6b:e4:5d:23:30:e7:c8:b9:86:ec:9d:73:e8: 06:43:6a:66:51:57:84:bd:75:1b:c8:4d:6b:9c:11: 79:36:bf:dd:d4:a8:0d:ce:6b:c3:d7:7e:0e:f5:b0: 78:c1:80:96:d5:45:73:ca:86:8e:7e:0f:85:43:6e: 26:0d:20:3a:72:12:80:73:60:a2:90:a1:13:30:27: d5:35 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 01:1E:40:80:D8:79:41:3D:8D:69:D6:E5:6C:DF:34:5D:8E:D7:07:D1 X509v3 Authority Key Identifier: keyid:03:E4:E7:DA:0F:64:DB:13:1E:BD:85:AB:76:BC:29:CA:2F:A7:C7:4B X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment X509v3 Subject Alternative Name: DNS:test-httpd, DNS:localhost Signature Algorithm: sha256WithRSAEncryption 88:be:99:32:13:9b:3f:89:59:65:19:2a:0e:1e:7d:9f:29:c5: d6:7e:82:db:18:2c:cb:b9:71:ef:ac:8b:31:0e:7c:b1:f9:7a: b5:60:2f:08:63:e1:1e:f5:d0:fe:e4:b7:4e:98:de:1b:01:22: 35:35:1c:ab:39:aa:25:d5:77:42:4c:eb:f6:d7:88:ba:14:27: 05:ae:08:b8:80:69:3c:e1:c6:d3:d1:26:1e:76:c7:a9:b2:2b: c3:2e:f6:27:db:3d:6c:2e:5c:ac:b1:2b:06:b0:8c:0b:74:3a: 72:dc:15:48:20:df:23:b1:2f:60:ba:e3:80:da:36:dc:aa:f6: 87:4a:c9:82:74:40:4a:f9:cc:95:d9:2b:2b:20:c8:fd:b5:87: 14:f6:13:1b:38:e6:7e:13:84:0b:c1:24:fe:dd:18:0c:ca:df: fb:71:5d:ea:aa:fb:ca:20:54:0b:7b:40:93:20:c5:4b:af:a6: 89:86:2f:49:d7:83:0e:4e:47:be:5f:f9:34:f9:38:7f:25:18: 05:0c:26:5e:aa:4c:c6:70:d2:27:5d:20:ef:8a:51:b6:86:8c: 66:26:3d:36:8b:b0:b9:e0:cb:17:22:a5:b6:30:a0:c4:ae:9f: 80:fb:7b:f1:55:f8:2f:61:b8:1e:f8:eb:2c:86:a3:53:6c:bd: c5:af:a2:1f -----BEGIN CERTIFICATE----- MIIEIDCCAwigAwIBAgIBATANBgkqhkiG9w0BAQsFADB6MSMwIQYDVQQKDBpBcGFj aGUgU29mdHdhcmUgRm91bmRhdGlvbjEfMB0GA1UECwwWSHR0cENvbXBvbmVudHMg UHJvamVjdDEQMA4GA1UEAwwHVGVzdCBDQTEgMB4GCSqGSIb3DQEJARYRZGV2QGhj LmFwYWNoZS5vcmcwIBcNMTcwMTE2MDk0MTQzWhgPMjExNjEyMjMwOTQxNDNaMH0x IzAhBgNVBAoMGkFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMR8wHQYDVQQLDBZI dHRwQ29tcG9uZW50cyBQcm9qZWN0MRMwEQYDVQQDDAp0ZXN0LWh0dHBkMSAwHgYJ KoZIhvcNAQkBFhFkZXZAaGMuYXBhY2hlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBANpVy3PDQs/BTm7ZdLj4HD8a3o1yOsRi9+vkclubnmUJDvSb 8L0p1a+p0V+CmVNJH3pcb2wPokhox1M+m5+ywuuPazjEanVSVWCdYECbpHnGx64c bNnItlvL1K94RQ5XYgRIHdLzwZisZB+ujTB47FKzA2xLHLGHVl6kwzxUawUilTDI DNTUQ/DrW1gpXM6Yl8yGeor9cA7AVVchLkr1Xb66bnaZasedn18xY5yutQN1bOzX 6HVr5F0jMOfIuYbsnXPoBkNqZlFXhL11G8hNa5wReTa/3dSoDc5rw9d+DvWweMGA ltVFc8qGjn4PhUNuJg0gOnISgHNgopChEzAn1TUCAwEAAaOBqzCBqDAJBgNVHRME AjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0 ZTAdBgNVHQ4EFgQUAR5AgNh5QT2NadblbN80XY7XB9EwHwYDVR0jBBgwFoAUA+Tn 2g9k2xMevYWrdrwpyi+nx0swCwYDVR0PBAQDAgXgMCAGA1UdEQQZMBeCCnRlc3Qt aHR0cGSCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAiL6ZMhObP4lZZRkq Dh59nynF1n6C2xgsy7lx76yLMQ58sfl6tWAvCGPhHvXQ/uS3TpjeGwEiNTUcqzmq JdV3Qkzr9teIuhQnBa4IuIBpPOHG09EmHnbHqbIrwy72J9s9bC5crLErBrCMC3Q6 ctwVSCDfI7EvYLrjgNo23Kr2h0rJgnRASvnMldkrKyDI/bWHFPYTGzjmfhOEC8Ek /t0YDMrf+3Fd6qr7yiBUC3tAkyDFS6+miYYvSdeDDk5Hvl/5NPk4fyUYBQwmXqpM xnDSJ10g74pRtoaMZiY9NouwueDLFyKltjCgxK6fgPt78VX4L2G4HvjrLIajU2y9 xa+iHw== -----END CERTIFICATE----- httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/apache-httpd/server-key.pem000066400000000000000000000032541434266521000313630ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDaVctzw0LPwU5u 2XS4+Bw/Gt6NcjrEYvfr5HJbm55lCQ70m/C9KdWvqdFfgplTSR96XG9sD6JIaMdT PpufssLrj2s4xGp1UlVgnWBAm6R5xseuHGzZyLZby9SveEUOV2IESB3S88GYrGQf ro0weOxSswNsSxyxh1ZepMM8VGsFIpUwyAzU1EPw61tYKVzOmJfMhnqK/XAOwFVX IS5K9V2+um52mWrHnZ9fMWOcrrUDdWzs1+h1a+RdIzDnyLmG7J1z6AZDamZRV4S9 dRvITWucEXk2v93UqA3Oa8PXfg71sHjBgJbVRXPKho5+D4VDbiYNIDpyEoBzYKKQ oRMwJ9U1AgMBAAECggEBAIEr4wthCUUKs5GHW7QXLfbzuZlrbHNFrjHEXRfvkJ1r 54o2PA5eEsszp+hexsFscJAe4djHwxYdz1djogSwaPueRSw3oFg61sIrOYffzUYy oW5T6N5MDf9vLyyE9i4O6rFnzSVCC1Z9H1tTFLsJv58Jw8utAJPTYvjpd4xY0Vwe SqT/ZdIB9Cb+3R46+yJdcUWFUqpzKXOWZ1JH1b3nOpaLeyXJX0Xau7oyTxh/8hjg +2DV9VI9LEKqzIV96iSsMzk28y9Iio7OW4x/vEDdpf9izmCSU8o2zLNHGBSvnUxM wHH3pO96fmOBwq/vQkwZ7I2Y+LiL0nrukUiGpaxBz0ECgYEA95LdkhdS+Ou8hEJt o9cILbTP+vK4NzhKhA1tRsWylNjuv8Bp/MwM1OvRInEwl05VenG3Fbm2AsDerQEQ b+aFPK+l78ZMlRZ945my9Q+jk3qYxmhl0j7mz+GdYt0MoKRyDe30wkk/cCPpwdhe GF8Tvk7EjfGoDFiuf8wCyMEF9+UCgYEA4cQrtQLztRBZBeO+r0Wx1r6AjEKNBMPl FYHh9qfAsRqF452xa331ftNjyPV2vmGdjVTU0FQgbf6ZSK5kCqyVTk9QHZdt65ds vqdpOS9FocWZOV6qTaIVoSJY804ZZJKVB+97HrFOHxPnOn0uJvtk/3x0awOoSu0I TCU/MXNk8xECgYEAn48aGlPJ+AAGqb8eZp/p93s3J+dS7tPqwpzctuYnqGL/zLm4 FWN1Sa0KRoZo6Ltlv9qWQvxD4BZp7VpoO5Z4fJo/+f710IiEbjHa8rI3nI9A827J YO2FWKlyBAuvXcFeeLfKLYWDy0R6HaLTUiXE1bxyVYFP61Ukcd0MVlKBBDkCgYAM KC0WVS9cW6H/kDDvbThTUPTJGLhRPl8ylkjdqFDW+I+nHxGzsResGaPw6U7Yl5cN SjkfcrhAVApbAJEAhiSQD/NHdKUFn6TKa2deHe6I9IP4s+FFxumVQK07hMQXR1Fh GQMvNur2/3JfEuiOTtE0dLYsIQlJ55Ofzg2mEwmnkQKBgQCjtelLDrPJn8x/7NzW L4/5xv3zHsjKxpvyn6jmP0s9wz2Dxeh4VzMudflw3y0l02bMLyPacQcaU9l5DKgP WRGD7a9Yig0dx/gHCjX+QMdKvgNR6Pi7iJiD15THsZB3L1ZpzjNGr/ENxn/Xx6VC bCjMxWkkzDr0xYCRr3FzQ1iEkg== -----END PRIVATE KEY----- httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/docker-compose.yml000066400000000000000000000022001434266521000276450ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: '3.5' services: test-httpd: container_name: "my-httpclient-tests-httpd" image: "httpclient-tests-httpd:latest" ports: - "8080:8080" - "8443:8443" test-squid: container_name: "my-httpclient-tests-squid" image: "httpclient-tests-squid:latest" ports: - "8888:8888" - "8889:8889" links: - "test-httpd"httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/squid/000077500000000000000000000000001434266521000253435ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/squid/Dockerfile000066400000000000000000000020151434266521000273330ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM sameersbn/squid:3.3.8-22 MAINTAINER dev@hc.apache.org ENV conf_dir /etc/squid3 RUN apt-get update RUN apt-get install -y apache2-utils COPY squid.conf ${conf_dir}/ RUN htpasswd -b -c ${conf_dir}/htpasswd squid nopassword EXPOSE 8888 EXPOSE 8889 httpcomponents-client-rel-v5.2.1/httpclient5-testing/docker/squid/squid.conf000066400000000000000000000046671434266521000273540ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ========================================================================== http_port 8888 http_port 8889 coredump_dir /var/spool/squid3 auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid3/htpasswd auth_param basic realm test-proxy acl all src 0.0.0.0/0.0.0.0 acl localnet src 10.0.0.0/8 # RFC1918 possible internal network acl localnet src 172.16.0.0/12 # RFC1918 possible internal network acl localnet src 192.168.0.0/16 # RFC1918 possible internal network acl localnet src fc00::/7 # RFC 4193 local private network range acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines acl secure_port myport 8888 acl insecure_port myport 8889 acl SSL_ports port 443 acl SSL_ports port 8443 acl Safe_ports port 80 # http acl Safe_ports port 21 # ftp acl Safe_ports port 443 # https acl Safe_ports port 70 # gopher acl Safe_ports port 210 # wais acl Safe_ports port 1025-65535 # unregistered ports acl Safe_ports port 280 # http-mgmt acl Safe_ports port 488 # gss-http acl Safe_ports port 591 # filemaker acl Safe_ports port 777 # multiling http acl CONNECT method CONNECT acl authenticated proxy_auth REQUIRED http_access deny !Safe_ports http_access deny CONNECT !SSL_ports http_access allow localhost manager http_access deny manager http_access allow secure_port localnet http_access allow secure_port localhost http_access allow insecure_port authenticated http_access deny all http_reply_access allow all cache deny all refresh_pattern ^ftp: 1440 20% 10080 refresh_pattern ^gopher: 1440 0% 1440 refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 refresh_pattern (Release|Packages(.gz)*)$ 0 20% 2880 refresh_pattern . 0 20% 4320 httpcomponents-client-rel-v5.2.1/httpclient5-testing/pom.xml000066400000000000000000000105471434266521000242730ustar00rootroot00000000000000 4.0.0 org.apache.httpcomponents.client5 httpclient5-parent 5.2.1 httpclient5-testing Apache HttpClient Integration Tests Apache HttpClient integration tests jar org.apache.httpcomponents.client5.httpclient5.testing org.apache.httpcomponents.core5 httpcore5-testing org.apache.httpcomponents.core5 httpcore5-reactive test org.apache.httpcomponents.client5 httpclient5 org.slf4j slf4j-api org.apache.logging.log4j log4j-slf4j-impl true org.apache.logging.log4j log4j-core true org.apache.httpcomponents.client5 httpclient5-cache test org.apache.httpcomponents.client5 httpclient5-fluent test org.apache.httpcomponents.client5 httpclient5-win test org.junit.jupiter junit-jupiter-params test org.hamcrest hamcrest test org.mockito mockito-core test io.reactivex.rxjava2 rxjava test maven-project-info-reports-plugin false index dependencies dependency-info summary httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/000077500000000000000000000000001434266521000235365ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/000077500000000000000000000000001434266521000244625ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/000077500000000000000000000000001434266521000254035ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/000077500000000000000000000000001434266521000261725ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/000077500000000000000000000000001434266521000274135ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/000077500000000000000000000000001434266521000300055ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/000077500000000000000000000000001434266521000313505ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/000077500000000000000000000000001434266521000330255ustar00rootroot00000000000000async/000077500000000000000000000000001434266521000340635ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testingAbstractSimpleServerExchangeHandler.java000066400000000000000000000105121434266521000437720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.io.IOException; import org.apache.hc.client5.http.async.methods.SimpleBody; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncRequestConsumer; import org.apache.hc.core5.http.nio.AsyncServerRequestHandler; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.AbstractAsyncRequesterConsumer; import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler; import org.apache.hc.core5.http.nio.support.BasicResponseProducer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; public abstract class AbstractSimpleServerExchangeHandler extends AbstractServerExchangeHandler { protected abstract SimpleHttpResponse handle(SimpleHttpRequest request, HttpCoreContext context) throws HttpException; @Override protected final AsyncRequestConsumer supplyConsumer( final HttpRequest request, final EntityDetails entityDetails, final HttpContext context) throws HttpException { return new AbstractAsyncRequesterConsumer(new BasicAsyncEntityConsumer()) { @Override protected SimpleHttpRequest buildResult( final HttpRequest request, final byte[] body, final ContentType contentType) { final SimpleRequestBuilder builder = SimpleRequestBuilder.copy(request); if (body != null) { builder.setBody(body, contentType); } return builder.build(); } }; } @Override protected final void handle( final SimpleHttpRequest request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final SimpleHttpResponse response = handle(request, HttpCoreContext.adapt(context)); final SimpleBody body = response.getBody(); final AsyncEntityProducer entityProducer; if (body != null) { if (body.isText()) { entityProducer = new StringAsyncEntityProducer(body.getBodyText(), body.getContentType()); } else { entityProducer = new BasicAsyncEntityProducer(body.getBodyBytes(), body.getContentType()); } } else { entityProducer = null; } responseTrigger.submitResponse(new BasicResponseProducer(response, entityProducer), context); } } AsyncEchoHandler.java000066400000000000000000000134271434266521000401070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Asserts; /** * A handler that echos the incoming request entity. */ public class AsyncEchoHandler implements AsyncServerExchangeHandler { private final BasicAsyncEntityConsumer entityConsumer; private final AtomicReference entityProducerRef; public AsyncEchoHandler() { this.entityConsumer = new BasicAsyncEntityConsumer(); this.entityProducerRef = new AtomicReference<>(); } @Override public void releaseResources() { entityConsumer.releaseResources(); final AsyncEntityProducer producer = entityProducerRef.getAndSet(null); if (producer != null) { producer.releaseResources(); } } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final String method = request.getMethod(); if (!"GET".equalsIgnoreCase(method) && !"HEAD".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method) && !"PUT".equalsIgnoreCase(method)) { throw new MethodNotSupportedException(method + " not supported by " + getClass().getName()); } if (entityDetails != null) { final ContentType contentType = ContentType.parseLenient(entityDetails.getContentType()); entityConsumer.streamStart(entityDetails, new FutureCallback() { @Override public void completed(final byte[] content) { final BasicAsyncEntityProducer entityProducer = new BasicAsyncEntityProducer(content, contentType); entityProducerRef.set(entityProducer); try { responseChannel.sendResponse(new BasicHttpResponse(HttpStatus.SC_OK), entityProducer, context); } catch (final IOException | HttpException ex) { failed(ex); } } @Override public void failed(final Exception ex) { releaseResources(); } @Override public void cancelled() { releaseResources(); } }); } else { responseChannel.sendResponse(new BasicHttpResponse(HttpStatus.SC_OK), null, context); entityConsumer.releaseResources(); } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { entityConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { entityConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { entityConsumer.streamEnd(trailers); } @Override public int available() { final AsyncEntityProducer producer = entityProducerRef.get(); Asserts.notNull(producer, "Entity producer"); return producer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { final AsyncEntityProducer producer = entityProducerRef.get(); Asserts.notNull(producer, "Entity producer"); producer.produce(channel); } @Override public void failed(final Exception cause) { releaseResources(); } } AsyncRandomHandler.java000066400000000000000000000170211434266521000404430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.StreamChannel; import org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityProducer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Asserts; /** * A handler that generates random data. */ public class AsyncRandomHandler implements AsyncServerExchangeHandler { private final AtomicReference entityProducerRef; public AsyncRandomHandler() { this.entityProducerRef = new AtomicReference<>(); } @Override public void releaseResources() { final AsyncEntityProducer producer = entityProducerRef.getAndSet(null); if (producer != null) { producer.releaseResources(); } } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final String method = request.getMethod(); if (!"GET".equalsIgnoreCase(method) && !"HEAD".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method) && !"PUT".equalsIgnoreCase(method)) { throw new MethodNotSupportedException(method + " not supported by " + getClass().getName()); } final URI uri; try { uri = request.getUri(); } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } final String path = uri.getPath(); final int slash = path.lastIndexOf('/'); if (slash != -1) { final String payload = path.substring(slash + 1); final long n; if (!payload.isEmpty()) { try { n = Long.parseLong(payload); } catch (final NumberFormatException ex) { throw new ProtocolException("Invalid request path: " + path); } } else { // random length, but make sure at least something is sent n = 1 + (int)(Math.random() * 79.0); } final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); final AsyncEntityProducer entityProducer = new RandomBinAsyncEntityProducer(n); entityProducerRef.set(entityProducer); responseChannel.sendResponse(response, entityProducer, context); } else { throw new ProtocolException("Invalid request path: " + path); } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { capacityChannel.update(Integer.MAX_VALUE); } @Override public void consume(final ByteBuffer src) throws IOException { } @Override public void streamEnd(final List trailers) throws HttpException, IOException { } @Override public int available() { final AsyncEntityProducer producer = entityProducerRef.get(); Asserts.notNull(producer, "Entity producer"); return producer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { final AsyncEntityProducer producer = entityProducerRef.get(); Asserts.notNull(producer, "Entity producer"); producer.produce(channel); } @Override public void failed(final Exception cause) { releaseResources(); } /** * An entity that generates random data. */ public static class RandomBinAsyncEntityProducer extends AbstractBinAsyncEntityProducer { /** The range from which to generate random data. */ private final static byte[] RANGE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" .getBytes(StandardCharsets.US_ASCII); /** The length of the random data to generate. */ private final long length; private long remaining; private final ByteBuffer buffer; public RandomBinAsyncEntityProducer(final long len) { super(512, ContentType.DEFAULT_TEXT); length = len; remaining = len; buffer = ByteBuffer.allocate(1024); } @Override public void releaseResources() { remaining = length; } @Override public boolean isRepeatable() { return true; } @Override public long getContentLength() { return length; } @Override public int availableData() { return Integer.MAX_VALUE; } @Override protected void produceData(final StreamChannel channel) throws IOException { final int chunk = Math.min((int) (remaining < Integer.MAX_VALUE ? remaining : Integer.MAX_VALUE), buffer.remaining()); for (int i = 0; i < chunk; i++) { final byte b = RANGE[(int) (Math.random() * RANGE.length)]; buffer.put(b); } remaining -= chunk; buffer.flip(); channel.write(buffer); buffer.compact(); if (remaining <= 0 && buffer.position() == 0) { channel.endStream(); } } @Override public void failed(final Exception cause) { } } } AuthenticatingAsyncDecorator.java000066400000000000000000000166201434266521000425430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.testing.auth.Authenticator; import org.apache.hc.client5.testing.auth.BasicAuthTokenExtractor; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncResponseProducer; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicResponseProducer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; public class AuthenticatingAsyncDecorator implements AsyncServerExchangeHandler { private final AsyncServerExchangeHandler exchangeHandler; private final Authenticator authenticator; private final AtomicReference responseProducerRef; private final BasicAuthTokenExtractor authTokenExtractor; public AuthenticatingAsyncDecorator(final AsyncServerExchangeHandler exchangeHandler, final Authenticator authenticator) { this.exchangeHandler = Args.notNull(exchangeHandler, "Request handler"); this.authenticator = Args.notNull(authenticator, "Authenticator"); this.responseProducerRef = new AtomicReference<>(); this.authTokenExtractor = new BasicAuthTokenExtractor(); } protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) { } @Override public void handleRequest( final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final Header h = request.getFirstHeader(HttpHeaders.AUTHORIZATION); final String challengeResponse = h != null ? authTokenExtractor.extract(h.getValue()) : null; final URIAuthority authority = request.getAuthority(); final String requestUri = request.getRequestUri(); final boolean authenticated = authenticator.authenticate(authority, requestUri, challengeResponse); final Header expect = request.getFirstHeader(HttpHeaders.EXPECT); final boolean expectContinue = expect != null && "100-continue".equalsIgnoreCase(expect.getValue()); if (authenticated) { if (expectContinue) { responseChannel.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE), context); } exchangeHandler.handleRequest(request, entityDetails, responseChannel, context); } else { final HttpResponse unauthorized = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED); final String realm = authenticator.getRealm(authority, requestUri); unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"" + realm + "\""); customizeUnauthorizedResponse(unauthorized); final AsyncResponseProducer responseProducer = new BasicResponseProducer( unauthorized, new BasicAsyncEntityProducer("Unauthorized", ContentType.TEXT_PLAIN)); responseProducerRef.set(responseProducer); responseProducer.sendResponse(responseChannel, context); } } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { exchangeHandler.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public final void consume(final ByteBuffer src) throws IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { exchangeHandler.consume(src); } } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { exchangeHandler.streamEnd(trailers); } } @Override public final int available() { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { return exchangeHandler.available(); } else { return responseProducer.available(); } } @Override public final void produce(final DataStreamChannel channel) throws IOException { final AsyncResponseProducer responseProducer = responseProducerRef.get(); if (responseProducer == null) { exchangeHandler.produce(channel); } else { responseProducer.produce(channel); } } @Override public final void failed(final Exception cause) { try { exchangeHandler.failed(cause); final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null); if (dataProducer != null) { dataProducer.failed(cause); } } finally { releaseResources(); } } @Override public final void releaseResources() { exchangeHandler.releaseResources(); final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null); if (dataProducer != null) { dataProducer.releaseResources(); } } } RedirectingAsyncDecorator.java000066400000000000000000000134501434266521000420310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.client5.testing.redirect.Redirect; import org.apache.hc.client5.testing.redirect.RedirectResolver; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; public class RedirectingAsyncDecorator implements AsyncServerExchangeHandler { private final AsyncServerExchangeHandler exchangeHandler; private final RedirectResolver redirectResolver; private final AtomicBoolean redirecting; public RedirectingAsyncDecorator(final AsyncServerExchangeHandler exchangeHandler, final RedirectResolver redirectResolver) { this.exchangeHandler = Args.notNull(exchangeHandler, "Exchange handler"); this.redirectResolver = redirectResolver; this.redirecting = new AtomicBoolean(); } private Redirect resolveRedirect(final HttpRequest request) throws HttpException { try { final URI requestURI = request.getUri(); return redirectResolver != null ? redirectResolver.resolve(requestURI) : null; } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } } private HttpResponse createRedirectResponse(final Redirect redirect) { final HttpResponse response = new BasicHttpResponse(redirect.status); if (redirect.location != null) { response.addHeader(new BasicHeader(HttpHeaders.LOCATION, redirect.location)); } switch (redirect.connControl) { case KEEP_ALIVE: response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.KEEP_ALIVE)); break; case CLOSE: response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE)); } return response; } @Override public void handleRequest(final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final Redirect redirect = resolveRedirect(request); if (redirect != null) { responseChannel.sendResponse(createRedirectResponse(redirect), null, context); redirecting.set(true); } else { exchangeHandler.handleRequest(request, entityDetails, responseChannel, context); } } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { if (!redirecting.get()) { exchangeHandler.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public final void consume(final ByteBuffer src) throws IOException { if (!redirecting.get()) { exchangeHandler.consume(src); } } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { if (!redirecting.get()) { exchangeHandler.streamEnd(trailers); } } @Override public int available() { if (!redirecting.get()) { return exchangeHandler.available(); } else { return 0; } } @Override public void produce(final DataStreamChannel channel) throws IOException { if (!redirecting.get()) { exchangeHandler.produce(channel); } } @Override public void failed(final Exception cause) { if (!redirecting.get()) { exchangeHandler.failed(cause); } } @Override public void releaseResources() { exchangeHandler.releaseResources(); } } ServiceUnavailableAsyncDecorator.java000066400000000000000000000125351434266521000433410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.ResponseChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; public class ServiceUnavailableAsyncDecorator implements AsyncServerExchangeHandler { private final AsyncServerExchangeHandler exchangeHandler; private final Resolver serviceAvailabilityResolver; private final AtomicBoolean serviceUnavailable; public ServiceUnavailableAsyncDecorator(final AsyncServerExchangeHandler exchangeHandler, final Resolver serviceAvailabilityResolver) { this.exchangeHandler = Args.notNull(exchangeHandler, "Exchange handler"); this.serviceAvailabilityResolver = Args.notNull(serviceAvailabilityResolver, "Service availability resolver"); this.serviceUnavailable = new AtomicBoolean(); } @Override public void handleRequest(final HttpRequest request, final EntityDetails entityDetails, final ResponseChannel responseChannel, final HttpContext context) throws HttpException, IOException { final TimeValue retryAfter = serviceAvailabilityResolver.resolve(request); serviceUnavailable.set(TimeValue.isPositive(retryAfter)); if (serviceUnavailable.get()) { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE); response.addHeader(HttpHeaders.RETRY_AFTER, Long.toString(retryAfter.toSeconds())); final ProtocolVersion version = request.getVersion(); if (version != null && version.compareToVersion(HttpVersion.HTTP_2) < 0) { response.addHeader(HttpHeaders.CONNECTION, "Close"); } responseChannel.sendResponse(response, null, context); } else { exchangeHandler.handleRequest(request, entityDetails, responseChannel, context); } } @Override public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException { if (!serviceUnavailable.get()) { exchangeHandler.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public final void consume(final ByteBuffer src) throws IOException { if (!serviceUnavailable.get()) { exchangeHandler.consume(src); } } @Override public final void streamEnd(final List trailers) throws HttpException, IOException { if (!serviceUnavailable.get()) { exchangeHandler.streamEnd(trailers); } } @Override public int available() { if (!serviceUnavailable.get()) { return exchangeHandler.available(); } else { return 0; } } @Override public void produce(final DataStreamChannel channel) throws IOException { if (!serviceUnavailable.get()) { exchangeHandler.produce(channel); } } @Override public void failed(final Exception cause) { if (!serviceUnavailable.get()) { exchangeHandler.failed(cause); } } @Override public void releaseResources() { exchangeHandler.releaseResources(); } } auth/000077500000000000000000000000001434266521000337075ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testingAuthenticator.java000066400000000000000000000026731434266521000373740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.auth; import org.apache.hc.core5.net.URIAuthority; public interface Authenticator { boolean authenticate(URIAuthority authority, String requestUri, String credentials); String getRealm(URIAuthority authority, String requestUri); } BasicAuthTokenExtractor.java000066400000000000000000000047331434266521000413210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.auth; import java.nio.charset.StandardCharsets; import org.apache.hc.client5.http.utils.Base64; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.ProtocolException; public class BasicAuthTokenExtractor { public String extract(final String challengeResponse) throws HttpException { if (challengeResponse != null) { final int i = challengeResponse.indexOf(' '); if (i == -1) { throw new ProtocolException("Invalid challenge response: " + challengeResponse); } final String schemeName = challengeResponse.substring(0, i); if (schemeName.equalsIgnoreCase(StandardAuthScheme.BASIC)) { final String s = challengeResponse.substring(i + 1).trim(); try { final byte[] credsRaw = s.getBytes(StandardCharsets.US_ASCII); final Base64 codec = new Base64(); return new String(codec.decode(credsRaw), StandardCharsets.US_ASCII); } catch (final IllegalArgumentException ex) { throw new ProtocolException("Malformed Basic credentials"); } } } return null; } } classic/000077500000000000000000000000001434266521000343675ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testingAuthenticatingDecorator.java000066400000000000000000000114351434266521000420500ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.classic; import java.io.IOException; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.testing.auth.Authenticator; import org.apache.hc.client5.testing.auth.BasicAuthTokenExtractor; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; public class AuthenticatingDecorator implements HttpServerRequestHandler { private final HttpServerRequestHandler requestHandler; private final Authenticator authenticator; private final BasicAuthTokenExtractor authTokenExtractor; public AuthenticatingDecorator(final HttpServerRequestHandler requestHandler, final Authenticator authenticator) { this.requestHandler = Args.notNull(requestHandler, "Request handler"); this.authenticator = Args.notNull(authenticator, "Authenticator"); this.authTokenExtractor = new BasicAuthTokenExtractor(); } protected void customizeUnauthorizedResponse(final ClassicHttpResponse unauthorized) { } @Override public void handle( final ClassicHttpRequest request, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final Header h = request.getFirstHeader(HttpHeaders.AUTHORIZATION); final String challengeResponse = h != null ? authTokenExtractor.extract(h.getValue()) : null; final URIAuthority authority = request.getAuthority(); final String requestUri = request.getRequestUri(); final boolean authenticated = authenticator.authenticate(authority, requestUri, challengeResponse); final Header expect = request.getFirstHeader(HttpHeaders.EXPECT); final boolean expectContinue = expect != null && "100-continue".equalsIgnoreCase(expect.getValue()); if (authenticated) { if (expectContinue) { responseTrigger.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE)); } requestHandler.handle(request, responseTrigger, context); } else { final ClassicHttpResponse unauthorized = new BasicClassicHttpResponse(HttpStatus.SC_UNAUTHORIZED); final String realm = authenticator.getRealm(authority, requestUri); unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"" + realm + "\""); customizeUnauthorizedResponse(unauthorized); if (unauthorized.getEntity() == null) { unauthorized.setEntity(new StringEntity("Unauthorized")); } if (expectContinue || request.getEntity() == null) { // Respond immediately responseTrigger.submitResponse(unauthorized); // Consume request body later EntityUtils.consume(request.getEntity()); } else { // Consume request body first EntityUtils.consume(request.getEntity()); // Respond later responseTrigger.submitResponse(unauthorized); } } } } EchoHandler.java000066400000000000000000000071301434266521000374070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.classic; import java.io.IOException; import java.util.Locale; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.protocol.HttpContext; /** * A handler that echos the incoming request entity. */ public class EchoHandler implements HttpRequestHandler { /** * 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 ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { final String method = request.getMethod().toUpperCase(Locale.ROOT); if (!"GET".equals(method) && !"HEAD".equals(method) && !"POST".equals(method) && !"PUT".equals(method)) { throw new MethodNotSupportedException(method + " not supported by " + getClass().getName()); } HttpEntity entity = 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; final ContentType contentType; if (entity == null) { data = new byte [0]; contentType = null; } else { data = EntityUtils.toByteArray(entity); final String contentTypeStr = entity.getContentType(); contentType = contentTypeStr == null ? null : ContentType.parse(contentTypeStr); } entity = new ByteArrayEntity(data, contentType); response.setCode(HttpStatus.SC_OK); response.setEntity(entity); } } RandomHandler.java000066400000000000000000000165511434266521000377600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.classic; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.MethodNotSupportedException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; import org.apache.hc.core5.http.protocol.HttpContext; /** * A handler that generates random data. */ public class RandomHandler implements HttpRequestHandler { /** * 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 ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { final String method = request.getMethod(); if (!"GET".equalsIgnoreCase(method) && !"HEAD".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method) && !"PUT".equalsIgnoreCase(method)) { throw new MethodNotSupportedException(method + " not supported by " + getClass().getName()); } final URI uri; try { uri = request.getUri(); } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } final String path = uri.getPath(); final int slash = path.lastIndexOf('/'); if (slash != -1) { final String payload = path.substring(slash + 1); final long n; if (!payload.isEmpty()) { try { n = Long.parseLong(payload); } catch (final NumberFormatException ex) { throw new ProtocolException("Invalid request path: " + path); } } else { // random length, but make sure at least something is sent n = 1 + (int)(Math.random() * 79.0); } response.setCode(HttpStatus.SC_OK); response.setEntity(new RandomEntity(n)); } else { throw new ProtocolException("Invalid request path: " + path); } } /** * An entity that generates random data. * This is an outgoing entity, it supports {@link #writeTo writeTo} * but not {@link #getContent getContent}. */ public static class RandomEntity extends AbstractHttpEntity { /** The range from which to generate random data. */ private final static byte[] RANGE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" .getBytes(StandardCharsets.US_ASCII); /** The length of the random data to generate. */ protected final long length; /** * Creates a new entity generating the given amount of data. * * @param len the number of random bytes to generate, * 0 to maxint */ public RandomEntity(final long len) { super((ContentType) null, null); length = len; } /** * Tells that this entity is not streaming. * * @return false */ @Override public final boolean isStreaming() { return false; } /** * Tells that this entity is repeatable, in a way. * Repetitions will generate different random data, * unless perchance the same random data is generated twice. * * @return {@code true} */ @Override public boolean isRepeatable() { return true; } /** * Obtains the size of the random data. * * @return the number of random bytes to generate */ @Override public long getContentLength() { return length; } /** * Not supported. * This method throws an exception. * * @return never anything */ @Override public InputStream getContent() { throw new UnsupportedOperationException(); } /** * Generates the random content. * * @param out where to write the content to */ @Override public void writeTo(final OutputStream out) throws IOException { final int blocksize = 2048; int remaining = (int) length; // range checked in constructor final byte[] data = new byte[Math.min(remaining, blocksize)]; while (remaining > 0) { final int end = Math.min(remaining, data.length); double value = 0.0; for (int i = 0; i < end; i++) { // 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]; } out.write(data, 0, end); out.flush(); remaining = remaining - end; } out.close(); } @Override public void close() throws IOException { } } } RedirectingDecorator.java000066400000000000000000000074251434266521000413440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.classic; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import org.apache.hc.client5.testing.redirect.Redirect; import org.apache.hc.client5.testing.redirect.RedirectResolver; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; public class RedirectingDecorator implements HttpServerRequestHandler { private final HttpServerRequestHandler requestHandler; private final RedirectResolver redirectResolver; public RedirectingDecorator(final HttpServerRequestHandler requestHandler, final RedirectResolver redirectResolver) { this.requestHandler = Args.notNull(requestHandler, "Request handler"); this.redirectResolver = redirectResolver; } @Override public void handle(final ClassicHttpRequest request, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { try { final URI requestURI = request.getUri(); final Redirect redirect = redirectResolver != null ? redirectResolver.resolve(requestURI) : null; if (redirect != null) { final ClassicHttpResponse response = new BasicClassicHttpResponse(redirect.status); if (redirect.location != null) { response.addHeader(new BasicHeader(HttpHeaders.LOCATION, redirect.location)); } switch (redirect.connControl) { case KEEP_ALIVE: response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.KEEP_ALIVE)); break; case CLOSE: response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE)); } responseTrigger.submitResponse(response); } else { requestHandler.handle(request, responseTrigger, context); } } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } } } redirect/000077500000000000000000000000001434266521000345475ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testingRedirect.java000066400000000000000000000033561434266521000371620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/redirect/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.redirect; public class Redirect { public enum ConnControl { PROTOCOL_DEFAULT, KEEP_ALIVE, CLOSE } public final int status; public final String location; public final ConnControl connControl; public Redirect(final int status, final String location, final ConnControl connControl) { this.status = status; this.location = location; this.connControl = connControl; } public Redirect(final int status, final String location) { this(status , location, ConnControl.PROTOCOL_DEFAULT); } } RedirectResolver.java000066400000000000000000000025641434266521000407040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/redirect/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.redirect; import java.net.URI; import java.net.URISyntaxException; public interface RedirectResolver { Redirect resolve(URI requestUri) throws URISyntaxException; } httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/000077500000000000000000000000001434266521000245155ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/000077500000000000000000000000001434266521000254365ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/000077500000000000000000000000001434266521000262255ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/000077500000000000000000000000001434266521000274465ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/000077500000000000000000000000001434266521000300405ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/000077500000000000000000000000001434266521000314035ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000323625ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/http/impl/000077500000000000000000000000001434266521000333235ustar00rootroot00000000000000win/000077500000000000000000000000001434266521000340415ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/http/implWindowsNegotiateSchemeGetTokenFail.java000066400000000000000000000037431434266521000435670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/http/impl/win/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.win; import com.sun.jna.platform.win32.Sspi; import com.sun.jna.platform.win32.Win32Exception; import com.sun.jna.platform.win32.WinError; public final class WindowsNegotiateSchemeGetTokenFail extends WindowsNegotiateScheme { public WindowsNegotiateSchemeGetTokenFail(final String schemeName, final String servicePrincipalName) { super(schemeName, servicePrincipalName); } @Override String getToken(final Sspi.CtxtHandle continueCtx, final Sspi.SecBufferDesc continueToken, final String targetName) { dispose(); /* We will rather throw SEC_E_TARGET_UNKNOWN because SEC_E_DOWNGRADE_DETECTED is not * available on Windows XP and this unit test always fails. */ throw new Win32Exception(WinError.SEC_E_TARGET_UNKNOWN); } } httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/000077500000000000000000000000001434266521000330605ustar00rootroot00000000000000BasicTestAuthenticator.java000066400000000000000000000036471434266521000402720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing; import java.util.Objects; import org.apache.hc.client5.testing.auth.Authenticator; import org.apache.hc.core5.net.URIAuthority; public class BasicTestAuthenticator implements Authenticator { private final String userToken; private final String realm; public BasicTestAuthenticator(final String userToken, final String realm) { this.userToken = userToken; this.realm = realm; } @Override public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) { return Objects.equals(userToken, credentials); } @Override public String getRealm(final URIAuthority authority, final String requestUri) { return realm; } } OldPathRedirectResolver.java000066400000000000000000000050541434266521000404070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing; import java.net.URI; import java.net.URISyntaxException; import org.apache.hc.client5.testing.redirect.Redirect; import org.apache.hc.client5.testing.redirect.RedirectResolver; import org.apache.hc.core5.net.URIBuilder; public class OldPathRedirectResolver implements RedirectResolver { private final String oldPath; private final String newPath; private final int status; private final Redirect.ConnControl connControl; public OldPathRedirectResolver( final String oldPath, final String newPath, final int status, final Redirect.ConnControl connControl) { this.oldPath = oldPath; this.newPath = newPath; this.status = status; this.connControl = connControl; } public OldPathRedirectResolver(final String oldPath, final String newPath, final int status) { this(oldPath, newPath, status, Redirect.ConnControl.PROTOCOL_DEFAULT); } @Override public Redirect resolve(final URI requestUri) throws URISyntaxException { final String path = requestUri.getPath(); if (path.startsWith(oldPath)) { final URI location = new URIBuilder(requestUri) .setPath(newPath + path.substring(oldPath.length())) .build(); return new Redirect(status, location.toString(), connControl); } return null; } } SSLTestContexts.java000066400000000000000000000040241434266521000366750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing; import javax.net.ssl.SSLContext; import org.apache.hc.core5.ssl.SSLContexts; public class SSLTestContexts { public static SSLContext createServerSSLContext() throws Exception { return SSLContexts.custom() .loadTrustMaterial(SSLTestContexts.class.getResource("/test.keystore"), "nopassword".toCharArray()) .loadKeyMaterial(SSLTestContexts.class.getResource("/test.keystore"), "nopassword".toCharArray(), "nopassword".toCharArray()) .build(); } public static SSLContext createClientSSLContext() throws Exception { return SSLContexts.custom() .loadTrustMaterial(SSLTestContexts.class.getResource("/test.keystore"), "nopassword".toCharArray()) .build(); } } async/000077500000000000000000000000001434266521000341165ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testingAbstractHttpAsyncClientAuthenticationTest.java000066400000000000000000000535651434266521000452770ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import java.util.Collections; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.stream.Collectors; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.auth.BasicAuthCache; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.BasicTestAuthenticator; import org.apache.hc.client5.testing.auth.Authenticator; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.support.BasicResponseBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.testing.nio.H2TestServer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public abstract class AbstractHttpAsyncClientAuthenticationTest extends AbstractIntegrationTestBase { public AbstractHttpAsyncClientAuthenticationTest(final URIScheme scheme) { super(scheme); } abstract protected H2TestServer startServer(final Decorator exchangeHandlerDecorator) throws Exception; protected H2TestServer startServer() throws Exception { return startServer(requestHandler -> new AuthenticatingAsyncDecorator(requestHandler, new BasicTestAuthenticator("test:test", "test realm"))); } interface TestClientBuilder { TestClientBuilder setDefaultAuthSchemeRegistry(Lookup authSchemeRegistry); TestClientBuilder setTargetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy); TestClientBuilder addResponseInterceptor(HttpResponseInterceptor responseInterceptor); TestClientBuilder addRequestInterceptor(HttpRequestInterceptor requestInterceptor); } abstract protected T startClientCustom(final Consumer clientCustomizer) throws Exception; T startClient() throws Exception { return startClientCustom(c -> {}); } @Test public void testBasicAuthenticationNoCreds() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationFailure() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "all-wrong".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationSuccess() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationWithEntitySuccess() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); final Future future = client.execute( SimpleRequestBuilder.put() .setHttpHost(target) .setPath("/") .setBody("Some important stuff", ContentType.TEXT_PLAIN) .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationExpectationFailure() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "all-wrong".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build()); final Future future = client.execute( SimpleRequestBuilder.put() .setHttpHost(target) .setPath("/") .setBody("Some important stuff", ContentType.TEXT_PLAIN) .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); } @Test public void testBasicAuthenticationExpectationSuccess() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build()); final Future future = client.execute( SimpleRequestBuilder.put() .setHttpHost(target) .setPath("/") .setBody("Some important stuff", ContentType.TEXT_PLAIN) .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationCredentialsCaching() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy()); final T client = startClientCustom(builder -> builder.setTargetAuthenticationStrategy(authStrategy)); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(target, "test", "test".toCharArray()) .build()); for (int i = 0; i < 5; i++) { final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); } Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testBasicAuthenticationCredentialsCachingByPathPrefix() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy()); final Queue responseQueue = new ConcurrentLinkedQueue<>(); final T client = startClientCustom(builder -> builder .setTargetAuthenticationStrategy(authStrategy) .addResponseInterceptor((response, entity, context) -> responseQueue.add(BasicResponseBuilder.copy(response).build()))); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(target, "test", "test".toCharArray()) .build(); final AuthCache authCache = new BasicAuthCache(); for (final String requestPath: new String[] {"/blah/a", "/blah/b?huh", "/blah/c", "/bl%61h/%61"}) { final HttpClientContext context = HttpClientContext.create(); context.setAuthCache(authCache); context.setCredentialsProvider(credentialsProvider); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath(requestPath) .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); } // There should be only single auth strategy call for all successful message exchanges Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any()); assertThat( responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()), CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200))); responseQueue.clear(); authCache.clear(); Mockito.reset(authStrategy); for (final String requestPath: new String[] {"/blah/a", "/yada/a", "/blah/blah/"}) { final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); context.setAuthCache(authCache); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath(requestPath) .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); } // There should be an auth strategy call for all successful message exchanges Mockito.verify(authStrategy, Mockito.times(3)).select(Mockito.any(), Mockito.any(), Mockito.any()); assertThat( responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()), CoreMatchers.equalTo(Arrays.asList(401, 200, 401, 200, 401, 200))); } @Test public void testAuthenticationUserinfoInRequestFailure() throws Exception { final H2TestServer server = startServer(); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute(SimpleRequestBuilder.get() .setScheme(target.getSchemeName()) .setAuthority(new URIAuthority("test:test", target.getHostName(), target.getPort())) .setPath("/") .build(), context, null); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> future.get()); assertThat(exception.getCause(), CoreMatchers.instanceOf(ProtocolException.class)); } @Test public void testReauthentication() throws Exception { final Authenticator authenticator = new BasicTestAuthenticator("test:test", "test realm") { private final AtomicLong count = new AtomicLong(0); @Override public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) { final boolean authenticated = super.authenticate(authority, requestUri, credentials); if (authenticated) { return this.count.incrementAndGet() % 4 != 0; } return false; } }; final H2TestServer server = startServer(exchangeHandler -> new AuthenticatingAsyncDecorator(exchangeHandler, authenticator) { @Override protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) { unauthorized.removeHeaders(HttpHeaders.WWW_AUTHENTICATE); unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\""); } }); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); final Registry authSchemeRegistry = RegistryBuilder.create() .register("MyBasic", context -> new BasicScheme() { private static final long serialVersionUID = 1L; @Override public String getName() { return "MyBasic"; } }) .build(); final T client = startClientCustom(builder -> builder.setDefaultAuthSchemeRegistry(authSchemeRegistry)); final RequestConfig config = RequestConfig.custom() .setTargetPreferredAuthSchemes(Collections.singletonList("MyBasic")) .build(); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); for (int i = 0; i < 10; i++) { final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); request.setConfig(config); final Future future = client.execute(request, context, null); final SimpleHttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); } } @Test public void testAuthenticationFallback() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new AuthenticatingAsyncDecorator(exchangeHandler, new BasicTestAuthenticator("test:test", "test realm")) { @Override protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) { unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"test realm\" invalid"); } }); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(), context, null); final SimpleHttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } } AbstractHttpAsyncFundamentalsTest.java000066400000000000000000000252501434266521000435700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.util.LinkedList; import java.util.Queue; import java.util.Random; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.testing.nio.H2TestServer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public abstract class AbstractHttpAsyncFundamentalsTest extends AbstractIntegrationTestBase { protected AbstractHttpAsyncFundamentalsTest(final URIScheme scheme) { super(scheme); } abstract protected H2TestServer startServer() throws Exception; abstract protected T startClient() throws Exception; @Test public void testSequentialGetRequests() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); for (int i = 0; i < 3; i++) { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response = future.get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final String body = response.getBodyText(); assertThat(body, CoreMatchers.notNullValue()); assertThat(body.length(), CoreMatchers.equalTo(2048)); } } @Test public void testSequentialHeadRequests() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); for (int i = 0; i < 3; i++) { final Future future = client.execute( SimpleRequestBuilder.head() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response = future.get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final String body = response.getBodyText(); assertThat(body, CoreMatchers.nullValue()); } } @Test public void testSequentialPostRequests() throws Exception { final H2TestServer server = startServer(); server.register("/echo/*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); for (int i = 0; i < 3; i++) { final byte[] b1 = new byte[1024]; final Random rnd = new Random(System.currentTimeMillis()); rnd.nextBytes(b1); final Future> future = client.execute( new BasicRequestProducer(Method.GET, target, "/echo/", AsyncEntityProducers.create(b1, ContentType.APPLICATION_OCTET_STREAM)), new BasicResponseConsumer<>(new BasicAsyncEntityConsumer()), HttpClientContext.create(), null); final Message responseMessage = future.get(); assertThat(responseMessage, CoreMatchers.notNullValue()); final HttpResponse response = responseMessage.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final byte[] b2 = responseMessage.getBody(); assertThat(b1, CoreMatchers.equalTo(b2)); } } @Test public void testConcurrentPostRequests() throws Exception { final H2TestServer server = startServer(); server.register("/echo/*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final byte[] b1 = new byte[1024]; final Random rnd = new Random(System.currentTimeMillis()); rnd.nextBytes(b1); final int reqCount = 20; final Queue>> queue = new LinkedList<>(); for (int i = 0; i < reqCount; i++) { final Future> future = client.execute( new BasicRequestProducer(Method.POST, target, "/echo/", AsyncEntityProducers.create(b1, ContentType.APPLICATION_OCTET_STREAM)), new BasicResponseConsumer<>(new BasicAsyncEntityConsumer()), HttpClientContext.create(), null); queue.add(future); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message responseMessage = future.get(); assertThat(responseMessage, CoreMatchers.notNullValue()); final HttpResponse response = responseMessage.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final byte[] b2 = responseMessage.getBody(); assertThat(b1, CoreMatchers.equalTo(b2)); } } @Test public void testRequestExecutionFromCallback() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final int requestNum = 50; final AtomicInteger count = new AtomicInteger(requestNum); final Queue resultQueue = new ConcurrentLinkedQueue<>(); final CountDownLatch countDownLatch = new CountDownLatch(requestNum); final FutureCallback callback = new FutureCallback() { @Override public void completed(final SimpleHttpResponse result) { try { resultQueue.add(result); if (count.decrementAndGet() > 0) { client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), this); } } finally { countDownLatch.countDown(); } } @Override public void failed(final Exception ex) { countDownLatch.countDown(); } @Override public void cancelled() { countDownLatch.countDown(); } }; final int threadNum = 5; final ExecutorService executorService = Executors.newFixedThreadPool(threadNum); for (int i = 0; i < threadNum; i++) { executorService.execute(() -> { if (!Thread.currentThread().isInterrupted()) { client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), callback); } }); } assertThat(countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()), CoreMatchers.equalTo(true)); executorService.shutdownNow(); executorService.awaitTermination(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); for (;;) { final SimpleHttpResponse response = resultQueue.poll(); if (response == null) { break; } assertThat(response.getCode(), CoreMatchers.equalTo(200)); } } @Test public void testBadRequest() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/boom") .build(), null); final SimpleHttpResponse response = future.get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(400)); } } AbstractHttpAsyncRedirectsTest.java000066400000000000000000000665241434266521000431040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.net.InetSocketAddress; import java.net.URI; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.apache.hc.client5.http.CircularRedirectException; import org.apache.hc.client5.http.RedirectException; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.OldPathRedirectResolver; import org.apache.hc.client5.testing.SSLTestContexts; import org.apache.hc.client5.testing.redirect.Redirect; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.util.TimeValue; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public abstract class AbstractHttpAsyncRedirectsTest extends AbstractIntegrationTestBase { private final HttpVersion version; public AbstractHttpAsyncRedirectsTest(final URIScheme scheme, final HttpVersion version) { super(scheme); this.version = version; } abstract protected H2TestServer startServer(final Decorator exchangeHandlerDecorator) throws Exception; protected H2TestServer startServer() throws Exception { return startServer(null); } abstract protected T startClient() throws Exception; @Test public void testBasicRedirect300() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MULTIPLE_CHOICES))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getCode()); Assertions.assertEquals("/oldlocation/", request.getRequestUri()); } @Test public void testBasicRedirect301() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/100") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/100", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testBasicRedirect302() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/123") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/123", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testBasicRedirect302NoLocation() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, requestUri -> { final String path = requestUri.getPath(); if (path.startsWith("/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, null); } return null; })); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/100") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getCode()); Assertions.assertEquals("/oldlocation/100", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testBasicRedirect303() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_SEE_OTHER))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/123") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/123", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testBasicRedirect304() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); server.register("/oldlocation/*", () -> new AbstractSimpleServerExchangeHandler() { @Override protected SimpleHttpResponse handle(final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException { return SimpleHttpResponse.create(HttpStatus.SC_NOT_MODIFIED, (String) null); } }); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, response.getCode()); Assertions.assertEquals("/oldlocation/", request.getRequestUri()); } @Test public void testBasicRedirect305() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); server.register("/oldlocation/*", () -> new AbstractSimpleServerExchangeHandler() { @Override protected SimpleHttpResponse handle(final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException { return SimpleHttpResponse.create(HttpStatus.SC_USE_PROXY, (String) null); } }); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_USE_PROXY, response.getCode()); Assertions.assertEquals("/oldlocation/", request.getRequestUri()); } @Test public void testBasicRedirect307() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_TEMPORARY_REDIRECT))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/123") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/123", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testMaxRedirectCheck() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/", HttpStatus.SC_MOVED_TEMPORARILY))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final RequestConfig config = RequestConfig.custom() .setCircularRedirectsAllowed(true) .setMaxRedirects(5).build(); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> { final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/circular-oldlocation/") .setRequestConfig(config) .build(), null); future.get(); }); assertThat(exception.getCause(), CoreMatchers.instanceOf(RedirectException.class)); } @Test public void testCircularRedirect() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/", HttpStatus.SC_MOVED_TEMPORARILY))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final RequestConfig config = RequestConfig.custom() .setCircularRedirectsAllowed(false) .build(); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/circular-oldlocation/") .setRequestConfig(config) .build(), null); future.get(); }); assertThat(exception.getCause(), CoreMatchers.instanceOf(CircularRedirectException.class)); } @Test public void testPostRedirect() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/echo", HttpStatus.SC_TEMPORARY_REDIRECT))); server.register("/echo/*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.post() .setHttpHost(target) .setPath("/oldlocation/stuff") .setBody("stuff", ContentType.TEXT_PLAIN) .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/echo/stuff", request.getRequestUri()); Assertions.assertEquals("POST", request.getMethod()); } @Test public void testPostRedirectSeeOther() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/echo", HttpStatus.SC_SEE_OTHER))); server.register("/echo/*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.post() .setHttpHost(target) .setPath("/oldlocation/stuff") .setBody("stuff", ContentType.TEXT_PLAIN) .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/echo/stuff", request.getRequestUri()); Assertions.assertEquals("GET", request.getMethod()); } @Test public void testRelativeRedirect() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, requestUri -> { final String path = requestUri.getPath(); if (path.startsWith("/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/random/100"); } return null; })); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/stuff") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/100", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testRelativeRedirect2() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, requestUri -> { final String path = requestUri.getPath(); if (path.equals("/random/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "100"); } return null; })); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/oldlocation") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/100", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testRejectBogusRedirectLocation() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, requestUri -> { final String path = requestUri.getPath(); if (path.equals("/oldlocation/")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "xxx://bogus"); } return null; })); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/") .build(), null); future.get(); }); assertThat(exception.getCause(), CoreMatchers.instanceOf(HttpException.class)); } @Test public void testRejectInvalidRedirectLocation() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, requestUri -> { final String path = requestUri.getPath(); if (path.equals("/oldlocation/")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/newlocation/?p=I have spaces"); } return null; })); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/") .build(), null); future.get(); }); assertThat(exception.getCause(), CoreMatchers.instanceOf(ProtocolException.class)); } @Test public void testRedirectWithCookie() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY))); final CookieStore cookieStore = new BasicCookieStore(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); 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 Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/100") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/100", request.getRequestUri()); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertEquals(1, headers.length, "There can only be one (cookie)"); } @Test public void testCrossSiteRedirect() throws Exception { final URIScheme scheme = scheme(); final H2TestServer secondServer = new H2TestServer(IOReactorConfig.DEFAULT, scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null); try { secondServer.register("/random/*", AsyncRandomHandler::new); final InetSocketAddress address2; if (version.greaterEquals(HttpVersion.HTTP_2)) { address2 = secondServer.start(H2Config.DEFAULT); } else { address2 = secondServer.start(Http1Config.DEFAULT); } final HttpHost redirectTarget = new HttpHost(scheme.name(), "localhost", address2.getPort()); final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, requestUri -> { final String path = requestUri.getPath(); if (path.equals("/oldlocation")) { final URI location = new URIBuilder(requestUri) .setHttpHost(redirectTarget) .setPath("/random/100") .build(); return new Redirect(HttpStatus.SC_MOVED_PERMANENTLY, location.toString()); } return null; })); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final T client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/100", request.getRequestUri()); Assertions.assertEquals(redirectTarget, new HttpHost(request.getScheme(), request.getAuthority())); } finally { secondServer.shutdown(TimeValue.ofSeconds(5)); } } } AbstractHttpReactiveFundamentalsTest.java000066400000000000000000000370341434266521000442600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.hamcrest.MatcherAssert.assertThat; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.schedulers.Schedulers; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.reactive.ReactiveEntityProducer; import org.apache.hc.core5.reactive.ReactiveResponseConsumer; import org.apache.hc.core5.reactive.ReactiveServerExchangeHandler; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.testing.reactive.Reactive3TestUtils; import org.apache.hc.core5.testing.reactive.Reactive3TestUtils.StreamDescription; import org.apache.hc.core5.testing.reactive.ReactiveEchoProcessor; import org.apache.hc.core5.testing.reactive.ReactiveRandomProcessor; import org.apache.hc.core5.util.TextUtils; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.reactivestreams.Publisher; public abstract class AbstractHttpReactiveFundamentalsTest extends AbstractIntegrationTestBase { public AbstractHttpReactiveFundamentalsTest(final URIScheme scheme) { super(scheme); } abstract protected H2TestServer startServer() throws Exception; abstract protected T startClient() throws Exception; @Test @Timeout(value = 60_000, unit = MILLISECONDS) public void testSequentialGetRequests() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", () -> new ReactiveServerExchangeHandler(new ReactiveRandomProcessor())); final HttpHost target = targetHost(); final T client = startClient(); for (int i = 0; i < 3; i++) { final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); client.execute(AsyncRequestBuilder.get(target + "/random/2048").build(), consumer, null); final Message> response = consumer.getResponseFuture().get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getHead().getCode(), CoreMatchers.equalTo(200)); final String body = publisherToString(response.getBody()); assertThat(body, CoreMatchers.notNullValue()); assertThat(body.length(), CoreMatchers.equalTo(2048)); } } @Test @Timeout(value = 2000, unit = MILLISECONDS) public void testSequentialHeadRequests() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", () -> new ReactiveServerExchangeHandler(new ReactiveRandomProcessor())); final HttpHost target = targetHost(); final T client = startClient(); for (int i = 0; i < 3; i++) { final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); client.execute(AsyncRequestBuilder.head(target + "/random/2048").build(), consumer, null); final Message> response = consumer.getResponseFuture().get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getHead().getCode(), CoreMatchers.equalTo(200)); final String body = publisherToString(response.getBody()); assertThat(body, CoreMatchers.nullValue()); } } @Test @Timeout(value = 60_000, unit = MILLISECONDS) public void testSequentialPostRequests() throws Exception { final H2TestServer server = startServer(); server.register("/echo/*", () -> new ReactiveServerExchangeHandler(new ReactiveEchoProcessor())); final HttpHost target = targetHost(); final T client = startClient(); for (int i = 0; i < 3; i++) { final byte[] b1 = new byte[1024]; final Random rnd = new Random(System.currentTimeMillis()); rnd.nextBytes(b1); final Flowable publisher = Flowable.just(ByteBuffer.wrap(b1)); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); final AsyncRequestProducer request = AsyncRequestBuilder.post(target + "/echo/") .setEntity(new ReactiveEntityProducer(publisher, -1, ContentType.APPLICATION_OCTET_STREAM, null)) .build(); client.execute(request, consumer, HttpClientContext.create(), null); final Future>> responseFuture = consumer.getResponseFuture(); final Message> responseMessage = responseFuture.get(); assertThat(responseMessage, CoreMatchers.notNullValue()); final HttpResponse response = responseMessage.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final byte[] b2 = publisherToByteArray(responseMessage.getBody()); assertThat(b1, CoreMatchers.equalTo(b2)); } } @Test @Timeout(value = 60_000, unit = MILLISECONDS) public void testConcurrentPostRequests() throws Exception { final H2TestServer server = startServer(); server.register("/echo/*", () -> new ReactiveServerExchangeHandler(new ReactiveEchoProcessor())); final HttpHost target = targetHost(); final T client = startClient(); final int reqCount = 500; final int maxSize = 128 * 1024; final Map testCases = StreamingTestCase.generate(reqCount, maxSize); final BlockingQueue responses = new ArrayBlockingQueue<>(reqCount); for (final StreamingTestCase testCase : testCases.values()) { final ReactiveEntityProducer producer = new ReactiveEntityProducer(testCase.stream, testCase.length, ContentType.APPLICATION_OCTET_STREAM, null); final AsyncRequestProducer request = AsyncRequestBuilder.post(target + "/echo/") .setEntity(producer) .build(); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(new FutureCallback>>() { @Override public void completed(final Message> result) { final Flowable flowable = Flowable.fromPublisher(result.getBody()) .observeOn(Schedulers.io()); // Stream the data on an RxJava scheduler, not a client thread Reactive3TestUtils.consumeStream(flowable).subscribe(responses::add); } @Override public void failed(final Exception ex) { } @Override public void cancelled() { } }); client.execute(request, consumer, HttpClientContext.create(), null); } for (int i = 0; i < reqCount; i++) { final StreamDescription streamDescription = responses.take(); final StreamingTestCase streamingTestCase = testCases.get(streamDescription.length); final long expectedLength = streamingTestCase.length; final long actualLength = streamDescription.length; Assertions.assertEquals(expectedLength, actualLength); final String expectedHash = streamingTestCase.expectedHash.get(); final String actualHash = TextUtils.toHexString(streamDescription.md.digest()); Assertions.assertEquals(expectedHash, actualHash); } } @Test @Timeout(value = 60_000, unit = MILLISECONDS) public void testRequestExecutionFromCallback() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", () -> new ReactiveServerExchangeHandler(new ReactiveRandomProcessor())); final HttpHost target = targetHost(); final T client = startClient(); final int requestNum = 50; final AtomicInteger count = new AtomicInteger(requestNum); final Queue>> resultQueue = new ConcurrentLinkedQueue<>(); final CountDownLatch countDownLatch = new CountDownLatch(requestNum); final FutureCallback>> callback = new FutureCallback>>() { @Override public void completed(final Message> result) { try { resultQueue.add(result); if (count.decrementAndGet() > 0) { final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(this); client.execute(AsyncRequestBuilder.get(target + "/random/2048").build(), consumer, null); } } finally { countDownLatch.countDown(); } } @Override public void failed(final Exception ex) { countDownLatch.countDown(); } @Override public void cancelled() { countDownLatch.countDown(); } }; final int threadNum = 5; final ExecutorService executorService = Executors.newFixedThreadPool(threadNum); for (int i = 0; i < threadNum; i++) { executorService.execute(() -> { if (!Thread.currentThread().isInterrupted()) { final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(callback); client.execute(AsyncRequestBuilder.get(target + "/random/2048").build(), consumer, null); } }); } assertThat(countDownLatch.await(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()), CoreMatchers.equalTo(true)); executorService.shutdownNow(); executorService.awaitTermination(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); for (;;) { final Message> response = resultQueue.poll(); if (response == null) { break; } assertThat(response.getHead().getCode(), CoreMatchers.equalTo(200)); } } @Test public void testBadRequest() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", () -> new ReactiveServerExchangeHandler(new ReactiveRandomProcessor())); final HttpHost target = targetHost(); final T client = startClient(); final AsyncRequestProducer request = AsyncRequestBuilder.get(target + "/random/boom").build(); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); client.execute(request, consumer, null); final Future>> future = consumer.getResponseFuture(); final HttpResponse response = future.get().getHead(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(400)); } static String publisherToString(final Publisher publisher) throws Exception { final byte[] bytes = publisherToByteArray(publisher); if (bytes == null) { return null; } return new String(bytes, StandardCharsets.UTF_8); } static byte[] publisherToByteArray(final Publisher publisher) throws Exception { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (WritableByteChannel channel = Channels.newChannel(baos)) { final List bufs = Flowable.fromPublisher(publisher) .toList() .blockingGet(); if (bufs.isEmpty()) { return null; } for (final ByteBuffer buf : bufs) { channel.write(buf); } } return baos.toByteArray(); } private static final class StreamingTestCase { final long length; final AtomicReference expectedHash; final Flowable stream; StreamingTestCase(final long length, final AtomicReference expectedHash, final Flowable stream) { this.length = length; this.expectedHash = expectedHash; this.stream = stream; } static Map generate(final int numTestCases, final int maxSize) { final Map testCases = new LinkedHashMap<>(); int testCaseNum = 0; while (testCases.size() < numTestCases) { final long seed = 198723L * testCaseNum++; final int length = 1 + new Random(seed).nextInt(maxSize); final AtomicReference expectedHash = new AtomicReference<>(); final Flowable stream = Reactive3TestUtils.produceStream(length, expectedHash); final StreamingTestCase streamingTestCase = new StreamingTestCase(length, expectedHash, stream); testCases.put((long) length, streamingTestCase); } return testCases; } } } AbstractIntegrationTestBase.java000066400000000000000000000121511434266521000423630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.util.function.Consumer; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder; import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; import org.apache.hc.client5.http.impl.async.MinimalH2AsyncClient; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.testing.async.extension.TestAsyncResources; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; public abstract class AbstractIntegrationTestBase { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private final TestAsyncResources testResources; protected AbstractIntegrationTestBase(final URIScheme scheme) { this.testResources = new TestAsyncResources(scheme, TIMEOUT); } public URIScheme scheme() { return testResources.scheme(); } public H2TestServer startServer( final H2Config h2Config, final HttpProcessor httpProcessor, final Decorator exchangeHandlerDecorator) throws Exception { return testResources.startServer(h2Config, httpProcessor, exchangeHandlerDecorator); } public H2TestServer startServer( final Http1Config http1Config, final HttpProcessor httpProcessor, final Decorator exchangeHandlerDecorator) throws Exception { return testResources.startServer(http1Config, httpProcessor, exchangeHandlerDecorator); } public HttpHost targetHost() { return testResources.targetHost(); } public CloseableHttpAsyncClient startClient( final Consumer connManagerCustomizer, final Consumer clientCustomizer) throws Exception { return testResources.startClient(connManagerCustomizer, clientCustomizer); } public CloseableHttpAsyncClient startClient( final Consumer clientCustomizer) throws Exception { return testResources.startClient(clientCustomizer); } public PoolingAsyncClientConnectionManager connManager() { return testResources.connManager(); } public CloseableHttpAsyncClient startH2Client( final Consumer clientCustomizer) throws Exception { return testResources.startH2Client(clientCustomizer); } public MinimalHttpAsyncClient startMinimalClient( final Http1Config http1Config, final H2Config h2Config, final Consumer connManagerCustomizer) throws Exception { return testResources.startMinimalClient(http1Config, h2Config, connManagerCustomizer); } public MinimalHttpAsyncClient startMinimalH2Client( final Http1Config http1Config, final H2Config h2Config, final Consumer connManagerCustomizer) throws Exception { return testResources.startMinimalClient(http1Config, h2Config, connManagerCustomizer); } public MinimalH2AsyncClient startMinimalH2Client(final H2Config h2Config) throws Exception { return testResources.startMinimalH2Client(h2Config); } } HttpIntegrationTests.java000066400000000000000000000131761434266521000411370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class HttpIntegrationTests { @Nested @DisplayName("Fundamentals (HTTP/1.1)") public class Http1 extends TestHttp1Async { public Http1() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/1.1, TLS)") public class Http1Tls extends TestHttp1Async { public Http1Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Fundamentals (HTTP/2)") public class H2 extends TestH2Async { public H2() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/2, TLS)") public class H2Tls extends TestH2Async { public H2Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Request re-execution (HTTP/1.1)") public class Http1RequestReExecution extends TestHttp1RequestReExecution { public Http1RequestReExecution() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Request re-execution (HTTP/1.1, TLS)") public class Http1RequestReExecutionTls extends TestHttp1RequestReExecution { public Http1RequestReExecutionTls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("HTTP protocol policy (HTTP/1.1)") public class Http1ProtocolPolicy extends TestHttpAsyncProtocolPolicy { public Http1ProtocolPolicy() throws Exception { super(URIScheme.HTTP, HttpVersion.HTTP_1_1); } } @Nested @DisplayName("HTTP protocol policy (HTTP/1.1, TLS)") public class Http1ProtocolPolicyTls extends TestHttpAsyncProtocolPolicy { public Http1ProtocolPolicyTls() throws Exception { super(URIScheme.HTTPS, HttpVersion.HTTP_1_1); } } @Nested @DisplayName("HTTP protocol policy (HTTP/2)") public class H2ProtocolPolicy extends TestHttpAsyncProtocolPolicy { public H2ProtocolPolicy() throws Exception { super(URIScheme.HTTP, HttpVersion.HTTP_2); } } @Nested @DisplayName("HTTP protocol policy (HTTP/2, TLS)") public class H2ProtocolPolicyTls extends TestHttpAsyncProtocolPolicy { public H2ProtocolPolicyTls() throws Exception { super(URIScheme.HTTPS, HttpVersion.HTTP_2); } } @Nested @DisplayName("Redirects (HTTP/1.1)") public class RedirectsHttp1 extends TestHttp1AsyncRedirects { public RedirectsHttp1() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Redirects (HTTP/1.1, TLS)") public class RedirectsHttp1Tls extends TestHttp1AsyncRedirects { public RedirectsHttp1Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Redirects (HTTP/2)") public class RedirectsH2 extends TestH2AsyncRedirect { public RedirectsH2() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Redirects (HTTP/2, TLS)") public class RedirectsH2Tls extends TestH2AsyncRedirect { public RedirectsH2Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Client authentication (HTTP/1.1)") public class AuthenticationHttp1 extends TestHttp1ClientAuthentication { public AuthenticationHttp1() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Client authentication (HTTP/1.1, TLS)") public class AuthenticationHttp1Tls extends TestHttp1ClientAuthentication { public AuthenticationHttp1Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Client authentication (HTTP/2)") public class AuthenticationH2 extends TestH2ClientAuthentication { public AuthenticationH2() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Client authentication (HTTP/2, TLS)") public class AuthenticationH2Tls extends TestH2ClientAuthentication { public AuthenticationH2Tls() throws Exception { super(URIScheme.HTTPS); } } }HttpMinimalIntegrationTests.java000066400000000000000000000042641434266521000424440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class HttpMinimalIntegrationTests { @Nested @DisplayName("Fundamentals (HTTP/1.1)") public class Http1 extends TestHttp1AsyncMinimal { public Http1() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/1.1, TLS)") public class Http1Tls extends TestHttp1AsyncMinimal { public Http1Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Fundamentals (HTTP/2)") public class H2 extends TestH2AsyncMinimal { public H2() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/2, TLS)") public class H2Tls extends TestH2AsyncMinimal { public H2Tls() throws Exception { super(URIScheme.HTTPS); } } }ReactiveIntegrationTests.java000066400000000000000000000042411434266521000417530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class ReactiveIntegrationTests { @Nested @DisplayName("Fundamentals (HTTP/1.1)") public class Http1 extends TestHttp1Reactive { public Http1() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/1.1, TLS)") public class Http1Tls extends TestHttp1Reactive { public Http1Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Fundamentals (HTTP/2)") public class H2 extends TestH2Reactive { public H2() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/2, TLS)") public class H2Tls extends TestH2Reactive { public H2Tls() throws Exception { super(URIScheme.HTTPS); } } }ReactiveMinimalIntegrationTests.java000066400000000000000000000043041434266521000432620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.core5.http.URIScheme; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; public class ReactiveMinimalIntegrationTests { @Nested @DisplayName("Fundamentals (HTTP/1.1)") public class Http1 extends TestHttp1ReactiveMinimal { public Http1() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/1.1, TLS)") public class Http1Tls extends TestHttp1ReactiveMinimal { public Http1Tls() throws Exception { super(URIScheme.HTTPS); } } @Nested @DisplayName("Fundamentals (HTTP/2)") public class H2 extends TestH2ReactiveMinimal { public H2() throws Exception { super(URIScheme.HTTP); } } @Nested @DisplayName("Fundamentals (HTTP/2, TLS)") public class H2Tls extends TestH2ReactiveMinimal { public H2Tls() throws Exception { super(URIScheme.HTTPS); } } }TestH2Async.java000066400000000000000000000035601434266521000370740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; public abstract class TestH2Async extends AbstractHttpAsyncFundamentalsTest { public TestH2Async(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(H2Config.DEFAULT, null, null); } @Override protected CloseableHttpAsyncClient startClient() throws Exception { return startH2Client(b -> {}); } }TestH2AsyncMinimal.java000066400000000000000000000036021434266521000404000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.client5.http.impl.async.MinimalH2AsyncClient; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; public abstract class TestH2AsyncMinimal extends AbstractHttpAsyncFundamentalsTest { public TestH2AsyncMinimal(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(H2Config.DEFAULT, null, null); } @Override protected MinimalH2AsyncClient startClient() throws Exception { return startMinimalH2Client(H2Config.DEFAULT); } }TestH2AsyncRedirect.java000066400000000000000000000042051434266521000405530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; public abstract class TestH2AsyncRedirect extends AbstractHttpAsyncRedirectsTest { public TestH2AsyncRedirect(final URIScheme scheme) { super(scheme, HttpVersion.HTTP_2); } @Override protected H2TestServer startServer(final Decorator exchangeHandlerDecorator) throws Exception { return startServer(H2Config.DEFAULT, null, exchangeHandlerDecorator); } @Override protected CloseableHttpAsyncClient startClient() throws Exception { return startH2Client(b -> {}); } }TestH2ClientAuthentication.java000066400000000000000000000076361434266521000421450ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.util.function.Consumer; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; public abstract class TestH2ClientAuthentication extends AbstractHttpAsyncClientAuthenticationTest { public TestH2ClientAuthentication(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer(final Decorator exchangeHandlerDecorator) throws Exception { return startServer(H2Config.DEFAULT, null, exchangeHandlerDecorator); } @Override protected CloseableHttpAsyncClient startClientCustom(final Consumer clientCustomizer) throws Exception { return startH2Client(new Consumer() { @Override public void accept(final H2AsyncClientBuilder builder) { clientCustomizer.accept(new TestClientBuilder() { @Override public TestClientBuilder setDefaultAuthSchemeRegistry(final Lookup authSchemeRegistry) { builder.setDefaultAuthSchemeRegistry(authSchemeRegistry); return this; } @Override public TestClientBuilder setTargetAuthenticationStrategy(final AuthenticationStrategy targetAuthStrategy) { builder.setTargetAuthenticationStrategy(targetAuthStrategy); return this; } @Override public TestClientBuilder addResponseInterceptor(final HttpResponseInterceptor responseInterceptor) { builder.addResponseInterceptorLast(responseInterceptor); return this; } @Override public TestClientBuilder addRequestInterceptor(final HttpRequestInterceptor requestInterceptor) { builder.addRequestInterceptorFirst(requestInterceptor); return this; } }); } }); } }TestH2Reactive.java000066400000000000000000000035721434266521000375640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; public abstract class TestH2Reactive extends AbstractHttpReactiveFundamentalsTest { public TestH2Reactive(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(H2Config.DEFAULT, null, null); } @Override protected CloseableHttpAsyncClient startClient() throws Exception { return startH2Client(b -> {}); } } TestH2ReactiveMinimal.java000066400000000000000000000036151434266521000410710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import org.apache.hc.client5.http.impl.async.MinimalH2AsyncClient; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; public abstract class TestH2ReactiveMinimal extends AbstractHttpReactiveFundamentalsTest { public TestH2ReactiveMinimal(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(H2Config.DEFAULT, null, null); } @Override protected MinimalH2AsyncClient startClient() throws Exception { return startMinimalH2Client(H2Config.DEFAULT); } } TestHttp1Async.java000066400000000000000000000227251434266521000376270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.testing.nio.H2TestServer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; public abstract class TestHttp1Async extends AbstractHttpAsyncFundamentalsTest { public TestHttp1Async(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(Http1Config.DEFAULT, null, null); } @Override protected CloseableHttpAsyncClient startClient() throws Exception { return startClient(b -> {}); } @ParameterizedTest(name = "{displayName}; concurrent connections: {0}") @ValueSource(ints = {5, 1, 20}) public void testSequentialGetRequestsCloseConnection(final int concurrentConns) throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final PoolingAsyncClientConnectionManager connManager = connManager(); connManager.setDefaultMaxPerRoute(concurrentConns); connManager.setMaxTotal(100); for (int i = 0; i < 3; i++) { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE) .build(), null); final SimpleHttpResponse response = future.get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final String body = response.getBodyText(); assertThat(body, CoreMatchers.notNullValue()); assertThat(body.length(), CoreMatchers.equalTo(2048)); } } @Test public void testSharedPool() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final PoolingAsyncClientConnectionManager connManager = connManager(); final Future future1 = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response1 = future1.get(); assertThat(response1, CoreMatchers.notNullValue()); assertThat(response1.getCode(), CoreMatchers.equalTo(200)); final String body1 = response1.getBodyText(); assertThat(body1, CoreMatchers.notNullValue()); assertThat(body1.length(), CoreMatchers.equalTo(2048)); try (final CloseableHttpAsyncClient httpclient2 = HttpAsyncClients.custom() .setConnectionManager(connManager) .setConnectionManagerShared(true) .build()) { httpclient2.start(); final Future future2 = httpclient2.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response2 = future2.get(); assertThat(response2, CoreMatchers.notNullValue()); assertThat(response2.getCode(), CoreMatchers.equalTo(200)); final String body2 = response2.getBodyText(); assertThat(body2, CoreMatchers.notNullValue()); assertThat(body2.length(), CoreMatchers.equalTo(2048)); } final Future future3 = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response3 = future3.get(); assertThat(response3, CoreMatchers.notNullValue()); assertThat(response3.getCode(), CoreMatchers.equalTo(200)); final String body3 = response3.getBodyText(); assertThat(body3, CoreMatchers.notNullValue()); assertThat(body3.length(), CoreMatchers.equalTo(2048)); } @Test public void testRequestCancellation() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final PoolingAsyncClientConnectionManager connManager = connManager(); connManager.setDefaultMaxPerRoute(1); connManager.setMaxTotal(1); final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); try { for (int i = 0; i < 20; i++) { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/1000") .build(), null); executorService.schedule(new Runnable() { @Override public void run() { future.cancel(true); } }, i % 5, TimeUnit.MILLISECONDS); try { future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); } catch (final TimeoutException ex) { throw ex; } catch (final Exception ignore) { } } final Random rnd = new Random(); for (int i = 0; i < 20; i++) { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/1000") .build(), null); executorService.schedule(new Runnable() { @Override public void run() { future.cancel(true); } }, rnd.nextInt(200), TimeUnit.MILLISECONDS); try { future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); } catch (final TimeoutException ex) { throw ex; } catch (final Exception ignore) { } } for (int i = 0; i < 5; i++) { final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/1000") .build(), null); final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(200)); } } finally { executorService.shutdownNow(); } } }TestHttp1AsyncMinimal.java000066400000000000000000000114051434266521000411270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.util.LinkedList; import java.util.Queue; import java.util.Random; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public abstract class TestHttp1AsyncMinimal extends AbstractHttpAsyncFundamentalsTest { public TestHttp1AsyncMinimal(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(Http1Config.DEFAULT, null, null); } @Override protected MinimalHttpAsyncClient startClient() throws Exception { return startMinimalClient( Http1Config.DEFAULT, H2Config.DEFAULT, b -> {}); } @Test public void testConcurrentPostRequestsSameEndpoint() throws Exception { final H2TestServer server = startServer(); server.register("/echo/*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final MinimalHttpAsyncClient client = startClient(); final byte[] b1 = new byte[1024]; final Random rnd = new Random(System.currentTimeMillis()); rnd.nextBytes(b1); final int reqCount = 20; final Future endpointLease = client.lease(target, null); final AsyncClientEndpoint endpoint = endpointLease.get(5, TimeUnit.SECONDS); try { final Queue>> queue = new LinkedList<>(); for (int i = 0; i < reqCount; i++) { final Future> future = endpoint.execute( new BasicRequestProducer(Method.GET, target, "/echo/", AsyncEntityProducers.create(b1, ContentType.APPLICATION_OCTET_STREAM)), new BasicResponseConsumer<>(new BasicAsyncEntityConsumer()), HttpClientContext.create(), null); queue.add(future); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message responseMessage = future.get(); assertThat(responseMessage, CoreMatchers.notNullValue()); final HttpResponse response = responseMessage.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final byte[] b2 = responseMessage.getBody(); assertThat(b1, CoreMatchers.equalTo(b2)); endpoint.releaseAndReuse(); } } finally { endpoint.releaseAndDiscard(); } } }TestHttp1AsyncRedirects.java000066400000000000000000000154701434266521000414730ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.OldPathRedirectResolver; import org.apache.hc.client5.testing.redirect.Redirect; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.testing.nio.H2TestServer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Redirection test cases. */ public abstract class TestHttp1AsyncRedirects extends AbstractHttpAsyncRedirectsTest { public TestHttp1AsyncRedirects(final URIScheme scheme) { super(scheme, HttpVersion.HTTP_1_1); } @Override protected H2TestServer startServer(final Decorator exchangeHandlerDecorator) throws Exception { return startServer(Http1Config.DEFAULT, null, exchangeHandlerDecorator); } @Override protected CloseableHttpAsyncClient startClient() throws Exception { return startClient(b -> {}); } @Test public void testBasicRedirect300NoKeepAlive() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MULTIPLE_CHOICES, Redirect.ConnControl.CLOSE))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getCode()); Assertions.assertEquals("/oldlocation/", request.getRequestUri()); } @Test public void testBasicRedirect301NoKeepAlive() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY, Redirect.ConnControl.CLOSE))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/100") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/100", request.getRequestUri()); Assertions.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); } @Test public void testDefaultHeadersRedirect() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new RedirectingAsyncDecorator( exchangeHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY, Redirect.ConnControl.CLOSE))); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final List

defaultHeaders = new ArrayList<>(1); defaultHeaders.add(new BasicHeader(HttpHeaders.USER_AGENT, "my-test-client")); final CloseableHttpAsyncClient client = startClient(builder -> builder .setDefaultHeaders(defaultHeaders) ); final HttpClientContext context = HttpClientContext.create(); final Future future = client.execute(SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/oldlocation/123") .build(), context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); final HttpRequest request = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertEquals("/random/123", request.getRequestUri()); final Header header = request.getFirstHeader(HttpHeaders.USER_AGENT); Assertions.assertEquals("my-test-client", header.getValue()); } } TestHttp1AsyncStatefulConnManagement.java000066400000000000000000000250141434266521000441440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.testing.nio.H2TestServer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHttp1AsyncStatefulConnManagement extends AbstractIntegrationTestBase { public TestHttp1AsyncStatefulConnManagement() { super(URIScheme.HTTP); } protected H2TestServer startServer() throws Exception { return startServer(Http1Config.DEFAULT, null, null); } @Test public void testStatefulConnections() throws Exception { final H2TestServer server = startServer(); server.register("*", () -> new AbstractSimpleServerExchangeHandler() { @Override protected SimpleHttpResponse handle( final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException { final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK); response.setBody("Whatever", ContentType.TEXT_PLAIN); return response; } }); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(builer -> builer .setUserTokenHandler((route, context) -> context.getAttribute("user"))); 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 HttpClientContext context = HttpClientContext.create(); contexts[i] = context; workers[i] = new HttpWorker( "user" + i, context, requestCount, target, client); } for (final HttpWorker worker : workers) { worker.start(); } for (final HttpWorker worker : workers) { worker.join(TIMEOUT.toMilliseconds()); } for (final HttpWorker worker : workers) { final Exception ex = worker.getException(); if (ex != null) { throw ex; } Assertions.assertEquals(requestCount, worker.getCount()); } for (final HttpContext context : contexts) { final String state0 = (String) context.getAttribute("r0"); Assertions.assertNotNull(state0); for (int r = 1; r < requestCount; r++) { Assertions.assertEquals(state0, context.getAttribute("r" + r)); } } } static class HttpWorker extends Thread { private final String uid; private final HttpClientContext context; private final int requestCount; private final HttpHost target; private final CloseableHttpAsyncClient httpclient; private volatile Exception exception; private volatile int count; public HttpWorker( final String uid, final HttpClientContext context, final int requestCount, final HttpHost target, final CloseableHttpAsyncClient httpclient) { super(); this.uid = uid; this.context = context; this.requestCount = requestCount; this.target = target; this.httpclient = httpclient; this.count = 0; } public int getCount() { return count; } public Exception getException() { return exception; } @Override public void run() { try { context.setAttribute("user", uid); for (int r = 0; r < requestCount; r++) { final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); final Future future = httpclient.execute(request, null); future.get(); count++; final EndpointDetails endpointDetails = context.getEndpointDetails(); final String connuid = Integer.toHexString(System.identityHashCode(endpointDetails)); context.setAttribute("r" + r, connuid); } } catch (final Exception ex) { exception = ex; } } } @Test public void testRouteSpecificPoolRecylcing() throws Exception { final H2TestServer server = startServer(); server.register("*", () -> new AbstractSimpleServerExchangeHandler() { @Override protected SimpleHttpResponse handle( final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException { final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK); response.setBody("Whatever", ContentType.TEXT_PLAIN); return response; } }); // 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 HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(builer -> builer .setUserTokenHandler((route, context) -> context.getAttribute("user"))); final PoolingAsyncClientConnectionManager connManager = connManager(); final int maxConn = 2; // We build a client with 2 max active // connections, and 2 max per route. connManager.setMaxTotal(maxConn); connManager.setDefaultMaxPerRoute(maxConn); // Bottom of the pool : a *keep alive* connection to Route 1. final HttpContext context1 = new BasicHttpContext(); context1.setAttribute("user", "stuff"); final SimpleHttpRequest request1 = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); final Future future1 = client.execute(request1, context1, null); final HttpResponse response1 = future1.get(); Assertions.assertNotNull(response1); Assertions.assertEquals(200, response1.getCode()); // 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 SimpleHttpRequest request2 = SimpleRequestBuilder.get() .setScheme(target.getSchemeName()) .setAuthority(new URIAuthority("127.0.0.1", target.getPort())) .setPath("/") .build(); final Future future2 = client.execute(request2, context2, null); final HttpResponse response2 = future2.get(); Assertions.assertNotNull(response2); Assertions.assertEquals(200, response2.getCode()); // 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 SimpleHttpRequest request3 = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); final Future future3 = client.execute(request3, context3, null); final HttpResponse response3 = future3.get(); Assertions.assertNotNull(response3); Assertions.assertEquals(200, response3.getCode()); } } TestHttp1ClientAuthentication.java000066400000000000000000000146471434266521000426740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.util.concurrent.Future; import java.util.function.Consumer; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.BasicTestAuthenticator; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.testing.nio.H2TestServer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public abstract class TestHttp1ClientAuthentication extends AbstractHttpAsyncClientAuthenticationTest { public TestHttp1ClientAuthentication(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer(final Decorator exchangeHandlerDecorator) throws Exception { return startServer(Http1Config.DEFAULT, null, exchangeHandlerDecorator); } @Override protected CloseableHttpAsyncClient startClientCustom(final Consumer clientCustomizer) throws Exception { return startClient(new Consumer() { @Override public void accept(final HttpAsyncClientBuilder builder) { clientCustomizer.accept(new TestClientBuilder() { @Override public TestClientBuilder setDefaultAuthSchemeRegistry(final Lookup authSchemeRegistry) { builder.setDefaultAuthSchemeRegistry(authSchemeRegistry); return this; } @Override public TestClientBuilder setTargetAuthenticationStrategy(final AuthenticationStrategy targetAuthStrategy) { builder.setTargetAuthenticationStrategy(targetAuthStrategy); return this; } @Override public TestClientBuilder addResponseInterceptor(final HttpResponseInterceptor responseInterceptor) { builder.addResponseInterceptorLast(responseInterceptor); return this; } @Override public TestClientBuilder addRequestInterceptor(final HttpRequestInterceptor requestInterceptor) { builder.addRequestInterceptorFirst(requestInterceptor); return this; } }); } }); } @Test public void testBasicAuthenticationSuccessNonPersistentConnection() throws Exception { final H2TestServer server = startServer(exchangeHandler -> new AuthenticatingAsyncDecorator(exchangeHandler, new BasicTestAuthenticator("test:test", "test realm")) { @Override protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) { unauthorized.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } }); server.register("*", AsyncEchoHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); final Future future = client.execute(request, context, null); final HttpResponse response = future.get(); Assertions.assertNotNull(response); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } }TestHttp1Reactive.java000066400000000000000000000171201434266521000403050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.hamcrest.MatcherAssert.assertThat; import java.nio.ByteBuffer; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.support.AsyncRequestBuilder; import org.apache.hc.core5.reactive.ReactiveResponseConsumer; import org.apache.hc.core5.reactive.ReactiveServerExchangeHandler; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.testing.reactive.ReactiveRandomProcessor; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.reactivestreams.Publisher; public abstract class TestHttp1Reactive extends AbstractHttpReactiveFundamentalsTest { public TestHttp1Reactive(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(Http1Config.DEFAULT, null, null); } @Override protected CloseableHttpAsyncClient startClient() throws Exception { return startClient(b -> {}); } @ParameterizedTest(name = "{displayName}; concurrent connections: {0}") @ValueSource(ints = {5, 1, 20}) @Timeout(value = 60_000, unit = MILLISECONDS) public void testSequentialGetRequestsCloseConnection(final int concurrentConns) throws Exception { final H2TestServer server = startServer(); server.register("/random/*", () -> new ReactiveServerExchangeHandler(new ReactiveRandomProcessor())); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final PoolingAsyncClientConnectionManager connManager = connManager(); connManager.setDefaultMaxPerRoute(concurrentConns); connManager.setMaxTotal(100); for (int i = 0; i < 3; i++) { final SimpleHttpRequest get = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(); get.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); final AsyncRequestProducer request = AsyncRequestBuilder.get(target + "/random/2048").build(); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); client.execute(request, consumer, null); final Message> response = consumer.getResponseFuture().get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getHead().getCode(), CoreMatchers.equalTo(200)); final String body = publisherToString(response.getBody()); assertThat(body, CoreMatchers.notNullValue()); assertThat(body.length(), CoreMatchers.equalTo(2048)); } } @Test @Timeout(value = 60_000, unit = MILLISECONDS) public void testSharedPool() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", () -> new ReactiveServerExchangeHandler(new ReactiveRandomProcessor())); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(); final PoolingAsyncClientConnectionManager connManager = connManager(); final AsyncRequestProducer request1 = AsyncRequestBuilder.get(target + "/random/2048").build(); final ReactiveResponseConsumer consumer1 = new ReactiveResponseConsumer(); client.execute(request1, consumer1, null); final Message> response1 = consumer1.getResponseFuture().get(); assertThat(response1, CoreMatchers.notNullValue()); assertThat(response1.getHead(), CoreMatchers.notNullValue()); assertThat(response1.getHead().getCode(), CoreMatchers.equalTo(200)); final String body1 = publisherToString(response1.getBody()); assertThat(body1, CoreMatchers.notNullValue()); assertThat(body1.length(), CoreMatchers.equalTo(2048)); try (final CloseableHttpAsyncClient httpclient2 = HttpAsyncClients.custom() .setConnectionManager(connManager) .setConnectionManagerShared(true) .build()) { httpclient2.start(); final AsyncRequestProducer request2 = AsyncRequestBuilder.get(target + "/random/2048").build(); final ReactiveResponseConsumer consumer2 = new ReactiveResponseConsumer(); httpclient2.execute(request2, consumer2, null); final Message> response2 = consumer2.getResponseFuture().get(); assertThat(response2, CoreMatchers.notNullValue()); assertThat(response2.getHead().getCode(), CoreMatchers.equalTo(200)); final String body2 = publisherToString(response2.getBody()); assertThat(body2, CoreMatchers.notNullValue()); assertThat(body2.length(), CoreMatchers.equalTo(2048)); } final AsyncRequestProducer request3 = AsyncRequestBuilder.get(target + "/random/2048").build(); final ReactiveResponseConsumer consumer3 = new ReactiveResponseConsumer(); client.execute(request3, consumer3, null); final Message> response3 = consumer3.getResponseFuture().get(); assertThat(response3, CoreMatchers.notNullValue()); assertThat(response3.getHead().getCode(), CoreMatchers.equalTo(200)); final String body3 = publisherToString(response3.getBody()); assertThat(body3, CoreMatchers.notNullValue()); assertThat(body3.length(), CoreMatchers.equalTo(2048)); } } TestHttp1ReactiveMinimal.java000066400000000000000000000117241434266521000416200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.util.LinkedList; import java.util.Queue; import java.util.Random; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.reactive.ReactiveServerExchangeHandler; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.testing.reactive.ReactiveEchoProcessor; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public abstract class TestHttp1ReactiveMinimal extends AbstractHttpReactiveFundamentalsTest { public TestHttp1ReactiveMinimal(final URIScheme scheme) { super(scheme); } @Override protected H2TestServer startServer() throws Exception { return startServer(Http1Config.DEFAULT, null, null); } @Override protected MinimalHttpAsyncClient startClient() throws Exception { return startMinimalClient( Http1Config.DEFAULT, H2Config.DEFAULT, b -> {}); } @Test public void testConcurrentPostRequestsSameEndpoint() throws Exception { final H2TestServer server = startServer(); server.register("/echo/*", () -> new ReactiveServerExchangeHandler(new ReactiveEchoProcessor())); final HttpHost target = targetHost(); final MinimalHttpAsyncClient client = startClient(); final byte[] b1 = new byte[1024]; final Random rnd = new Random(System.currentTimeMillis()); rnd.nextBytes(b1); final int reqCount = 20; final Future endpointLease = client.lease(target, null); final AsyncClientEndpoint endpoint = endpointLease.get(5, TimeUnit.SECONDS); try { final Queue>> queue = new LinkedList<>(); for (int i = 0; i < reqCount; i++) { final Future> future = endpoint.execute( new BasicRequestProducer(Method.GET, target, "/echo/", AsyncEntityProducers.create(b1, ContentType.APPLICATION_OCTET_STREAM)), new BasicResponseConsumer<>(new BasicAsyncEntityConsumer()), HttpClientContext.create(), null); queue.add(future); } while (!queue.isEmpty()) { final Future> future = queue.remove(); final Message responseMessage = future.get(); assertThat(responseMessage, CoreMatchers.notNullValue()); final HttpResponse response = responseMessage.getHead(); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final byte[] b2 = responseMessage.getBody(); assertThat(b1, CoreMatchers.equalTo(b2)); endpoint.releaseAndReuse(); } } finally { endpoint.releaseAndDiscard(); } } } TestHttp1RequestReExecution.java000066400000000000000000000110631434266521000423460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.util.TimeValue; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public abstract class TestHttp1RequestReExecution extends AbstractIntegrationTestBase { public TestHttp1RequestReExecution(final URIScheme scheme) { super(scheme); } protected H2TestServer startServer() throws Exception { final Resolver serviceAvailabilityResolver = new Resolver() { private final AtomicInteger count = new AtomicInteger(0); @Override public TimeValue resolve(final HttpRequest request) { final int n = count.incrementAndGet(); return n <= 3 ? TimeValue.ofSeconds(1) : null; } }; return startServer(Http1Config.DEFAULT, null, handler -> new ServiceUnavailableAsyncDecorator(handler, serviceAvailabilityResolver)); } @Test public void testGiveUpAfterOneRetry() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(builder -> builder .setRetryStrategy(new DefaultHttpRequestRetryStrategy(1, TimeValue.ofSeconds(1)))); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response = future.get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_SERVICE_UNAVAILABLE)); } @Test public void testDoNotGiveUpEasily() throws Exception { final H2TestServer server = startServer(); server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final CloseableHttpAsyncClient client = startClient(builder -> builder .setRetryStrategy(new DefaultHttpRequestRetryStrategy(5, TimeValue.ofSeconds(1)))); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response = future.get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK)); } } TestHttpAsyncMinimalTlsHandshake.java000066400000000000000000000077761434266521000433600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import javax.net.ssl.SSLException; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.ssl.SSLContexts; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHttpAsyncMinimalTlsHandshake extends AbstractIntegrationTestBase { public TestHttpAsyncMinimalTlsHandshake() { super(URIScheme.HTTPS); } protected MinimalHttpAsyncClient startMinimalClient( final Consumer connManagerCustomizer) throws Exception { return startMinimalClient( Http1Config.DEFAULT, H2Config.DEFAULT, connManagerCustomizer); } @Test public void testSuccessfulTlsHandshake() throws Exception { startServer(Http1Config.DEFAULT, null, null); final HttpHost target = targetHost(); final int maxConnNo = 2; final MinimalHttpAsyncClient client = startMinimalClient(builder -> builder .setMaxConnPerRoute(maxConnNo) .setMaxConnTotal(maxConnNo)); for (int i = 0; i < maxConnNo + 1; i++) { final Future endpointLease = client.lease(target, null); final AsyncClientEndpoint endpoint = endpointLease.get(5, TimeUnit.SECONDS); endpoint.releaseAndDiscard(); } } @Test public void testTlsHandshakeFailure() throws Exception { startServer(Http1Config.DEFAULT, null, null); final HttpHost target = targetHost(); final int maxConnNo = 2; final MinimalHttpAsyncClient client = startMinimalClient(builder -> builder .setTlsStrategy(new DefaultClientTlsStrategy(SSLContexts.createDefault())) .setMaxConnPerRoute(maxConnNo) .setMaxConnTotal(maxConnNo)); for (int i = 0; i < maxConnNo + 1; i++) { final Future endpointLease = client.lease(target, null); final ExecutionException executionException = Assertions.assertThrows(ExecutionException.class, () -> endpointLease.get(5, TimeUnit.SECONDS)); Assertions.assertInstanceOf(SSLException.class, executionException.getCause()); } } }TestHttpAsyncProtocolPolicy.java000066400000000000000000000100611434266521000424360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async; import static org.hamcrest.MatcherAssert.assertThat; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.testing.nio.H2TestServer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; public abstract class TestHttpAsyncProtocolPolicy extends AbstractIntegrationTestBase { private final HttpVersion version; public TestHttpAsyncProtocolPolicy(final URIScheme scheme, final HttpVersion version) { super(scheme); this.version = version; } @Test public void testRequestContext() throws Exception { final H2TestServer server; if (version.greaterEquals(HttpVersion.HTTP_2)) { server = startServer(H2Config.DEFAULT, null, null); } else { server = startServer(Http1Config.DEFAULT, null, null); } server.register("/random/*", AsyncRandomHandler::new); final HttpHost target = targetHost(); final AtomicReference versionRef = new AtomicReference<>(); final CloseableHttpAsyncClient client = startClient( builder -> builder .setDefaultTlsConfig(TlsConfig.custom() .setVersionPolicy(version.greaterEquals(HttpVersion.HTTP_2) ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1) .build()), builder -> builder .addRequestInterceptorFirst((request, entity, context) -> versionRef.set(context.getProtocolVersion()) )); final Future future = client.execute( SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/random/2048") .build(), null); final SimpleHttpResponse response = future.get(); assertThat(response, CoreMatchers.notNullValue()); assertThat(response.getCode(), CoreMatchers.equalTo(200)); final String body = response.getBodyText(); assertThat(body, CoreMatchers.notNullValue()); assertThat(body.length(), CoreMatchers.equalTo(2048)); assertThat(versionRef.get(), CoreMatchers.equalTo(version)); } } extension/000077500000000000000000000000001434266521000361325ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/asyncTestAsyncResources.java000066400000000000000000000225551434266521000426160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/extension/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.async.extension; import java.net.InetSocketAddress; import java.util.function.Consumer; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder; import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.async.MinimalH2AsyncClient; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.client5.testing.SSLTestContexts; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestAsyncResources implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(TestClientResources.class); private final URIScheme scheme; private final Timeout timeout; private H2TestServer server; private InetSocketAddress socketAddress; private PoolingAsyncClientConnectionManager connManager; private CloseableHttpAsyncClient client; public TestAsyncResources(final URIScheme scheme, final Timeout timeout) { this.scheme = scheme; this.timeout = timeout; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); server = new H2TestServer( IOReactorConfig.custom() .setSoTimeout(timeout) .build(), scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test server"); if (client != null) { client.close(CloseMode.GRACEFUL); } if (connManager != null) { connManager.close(CloseMode.IMMEDIATE); } if (server != null) { server.shutdown(TimeValue.ofSeconds(5)); } } public URIScheme scheme() { return this.scheme; } public H2TestServer startServer( final H2Config h2Config, final HttpProcessor httpProcessor, final Decorator exchangeHandlerDecorator) throws Exception { Assertions.assertNotNull(server); socketAddress = server.start(httpProcessor, exchangeHandlerDecorator, h2Config); return server; } public H2TestServer startServer( final Http1Config http1Config, final HttpProcessor httpProcessor, final Decorator exchangeHandlerDecorator) throws Exception { Assertions.assertNotNull(server); socketAddress = server.start(httpProcessor, exchangeHandlerDecorator, http1Config); return server; } public HttpHost targetHost() { Assertions.assertNotNull(socketAddress); return new HttpHost(scheme.id, "localhost", socketAddress.getPort()); } public CloseableHttpAsyncClient startClient( final Consumer connManagerCustomizer, final Consumer clientCustomizer) throws Exception { Assertions.assertNull(connManager); Assertions.assertNull(client); final PoolingAsyncClientConnectionManagerBuilder connManagerBuilder = PoolingAsyncClientConnectionManagerBuilder.create(); connManagerBuilder.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext())); connManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom() .setSocketTimeout(timeout) .setConnectTimeout(timeout) .build()); connManagerCustomizer.accept(connManagerBuilder); connManager = connManagerBuilder.build(); final HttpAsyncClientBuilder clientBuilder = HttpAsyncClientBuilder.create() .setConnectionManager(connManager) .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(timeout) .build()); clientCustomizer.accept(clientBuilder); client = clientBuilder.build(); client.start(); return client; } public CloseableHttpAsyncClient startClient( final Consumer clientCustomizer) throws Exception { return startClient(b -> { }, clientCustomizer); } public PoolingAsyncClientConnectionManager connManager() { Assertions.assertNotNull(connManager); return connManager; } public CloseableHttpAsyncClient startH2Client( final Consumer clientCustomizer) throws Exception { Assertions.assertNull(connManager); Assertions.assertNull(client); final H2AsyncClientBuilder clientBuilder = H2AsyncClientBuilder.create(); clientBuilder.setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(timeout) .build()); clientBuilder.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext())); clientCustomizer.accept(clientBuilder); client = clientBuilder.build(); client.start(); return client; } public MinimalHttpAsyncClient startMinimalClient( final Http1Config http1Config, final H2Config h2Config, final Consumer connManagerCustomizer) throws Exception { Assertions.assertNull(connManager); Assertions.assertNull(client); final PoolingAsyncClientConnectionManagerBuilder connManagerBuilder = PoolingAsyncClientConnectionManagerBuilder.create(); connManagerBuilder.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext())); connManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom() .setSocketTimeout(timeout) .setConnectTimeout(timeout) .build()); connManagerCustomizer.accept(connManagerBuilder); connManager = connManagerBuilder.build(); final MinimalHttpAsyncClient minimal = HttpAsyncClients.createMinimal( h2Config, http1Config, IOReactorConfig.custom() .setSoTimeout(timeout) .build(), connManager); client = minimal; client.start(); return minimal; } public MinimalH2AsyncClient startMinimalH2Client(final H2Config h2Config) throws Exception { Assertions.assertNull(client); final MinimalH2AsyncClient minimal = HttpAsyncClients.createHttp2Minimal( h2Config, IOReactorConfig.custom() .setSoTimeout(timeout) .build(), new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext())); client = minimal; client.start(); return minimal; } } external/000077500000000000000000000000001434266521000346235ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testingCachingHttpAsyncClientCompatibilityTest.java000066400000000000000000000306461434266521000454220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.external; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.cache.CacheResponseStatus; import org.apache.hc.client5.http.cache.HttpCacheContext; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.cache.CacheConfig; import org.apache.hc.client5.http.impl.cache.CachingHttpAsyncClients; import org.apache.hc.client5.http.impl.cache.HeapResourceFactory; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; public class CachingHttpAsyncClientCompatibilityTest { public static void main(final String... args) throws Exception { final CachingHttpAsyncClientCompatibilityTest[] tests = new CachingHttpAsyncClientCompatibilityTest[] { new CachingHttpAsyncClientCompatibilityTest( HttpVersion.HTTP_1_1, new HttpHost("http", "localhost", 8080)), new CachingHttpAsyncClientCompatibilityTest( HttpVersion.HTTP_2_0, new HttpHost("http", "localhost", 8080)) }; for (final CachingHttpAsyncClientCompatibilityTest test: tests) { try { test.execute(); } finally { test.shutdown(); } } } private static final Timeout TIMEOUT = Timeout.ofSeconds(5); private final HttpVersion protocolVersion; private final HttpHost target; private final PoolingAsyncClientConnectionManager connManager; private final CloseableHttpAsyncClient client; CachingHttpAsyncClientCompatibilityTest(final HttpVersion protocolVersion, final HttpHost target) throws Exception { this.protocolVersion = protocolVersion; this.target = target; this.connManager = PoolingAsyncClientConnectionManagerBuilder.create() .setTlsStrategy(new DefaultClientTlsStrategy(SSLContexts.custom() .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()) .build())) .setDefaultTlsConfig(TlsConfig.custom() .setVersionPolicy(this.protocolVersion == HttpVersion.HTTP_2 ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1) .build()) .build(); this.client = CachingHttpAsyncClients.custom() .setCacheConfig(CacheConfig.custom() .setMaxObjectSize(20480) .build()) .setResourceFactory(HeapResourceFactory.INSTANCE) .setConnectionManager(this.connManager) .build(); } void shutdown() throws Exception { client.close(); } enum TestResult {OK, NOK} private void logResult(final TestResult result, final HttpRequest request, final String message) { final StringBuilder buf = new StringBuilder(); buf.append(result); if (buf.length() == 2) { buf.append(" "); } buf.append(": ").append(target); buf.append(": "); buf.append(request.getMethod()).append(" ").append(request.getRequestUri()); if (message != null && !TextUtils.isBlank(message)) { buf.append(" -> ").append(message); } System.out.println(buf); } void execute() throws Exception { client.start(); // Initial ping { final HttpCacheContext context = HttpCacheContext.create(); final SimpleHttpRequest options = SimpleRequestBuilder.options() .setHttpHost(target) .setPath("*") .build(); final Future future = client.execute(options, context, null); try { final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server"))); } else { logResult(TestResult.NOK, options, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, options, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, options, "(time out)"); } } // GET with links { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); final HttpCacheContext context = HttpCacheContext.create(); final Pattern linkPattern = Pattern.compile("^<(.*)>;rel=preload$"); final List links = new ArrayList<>(); final SimpleHttpRequest getRoot1 = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); final Future future1 = client.execute(getRoot1, context, null); try { final SimpleHttpResponse response = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) { logResult(TestResult.OK, getRoot1, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getRoot1, "(status " + code + ", " + cacheResponseStatus + ")"); } for (final Header header: response.getHeaders("Link")) { final Matcher matcher = linkPattern.matcher(header.getValue()); if (matcher.matches()) { links.add(matcher.group(1)); } } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, getRoot1, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, getRoot1, "(time out)"); } for (final String link: links) { final SimpleHttpRequest getLink = SimpleRequestBuilder.get() .setHttpHost(target) .setPath(link) .build(); final Future linkFuture = client.execute(getLink, context, null); try { final SimpleHttpResponse response = linkFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) { logResult(TestResult.OK, getLink, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getLink, "(status " + code + ", " + cacheResponseStatus + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, getLink, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, getLink, "(time out)"); } } final SimpleHttpRequest getRoot2 = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); final Future future2 = client.execute(getRoot2, context, null); try { final SimpleHttpResponse response = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) { logResult(TestResult.OK, getRoot2, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getRoot2, "(status " + code + ", " + cacheResponseStatus + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, getRoot2, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, getRoot2, "(time out)"); } for (final String link: links) { final SimpleHttpRequest getLink = SimpleRequestBuilder.get() .setHttpHost(target) .setPath(link) .build(); final Future linkFuture = client.execute(getLink, context, null); try { final SimpleHttpResponse response = linkFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) { logResult(TestResult.OK, getLink, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getLink, "(status " + code + ", " + cacheResponseStatus + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, getLink, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, getLink, "(time out)"); } } } } } CachingHttpClientCompatibilityTest.java000066400000000000000000000231131434266521000444130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.external; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLContext; import org.apache.hc.client5.http.cache.CacheResponseStatus; import org.apache.hc.client5.http.cache.HttpCacheContext; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpOptions; import org.apache.hc.client5.http.impl.cache.CacheConfig; import org.apache.hc.client5.http.impl.cache.CachingHttpClients; import org.apache.hc.client5.http.impl.cache.HeapResourceFactory; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; public class CachingHttpClientCompatibilityTest { public static void main(final String... args) throws Exception { final CachingHttpClientCompatibilityTest[] tests = new CachingHttpClientCompatibilityTest[] { new CachingHttpClientCompatibilityTest( new HttpHost("http", "localhost", 8080)) }; for (final CachingHttpClientCompatibilityTest test: tests) { try { test.execute(); } finally { test.shutdown(); } } } private final HttpHost target; private final PoolingHttpClientConnectionManager connManager; private final CloseableHttpClient client; CachingHttpClientCompatibilityTest(final HttpHost target) throws Exception { this.target = target; final SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build(); this.connManager = PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)) .build(); this.client = CachingHttpClients.custom() .setCacheConfig(CacheConfig.custom() .setMaxObjectSize(20480) .build()) .setResourceFactory(HeapResourceFactory.INSTANCE) .setConnectionManager(this.connManager) .build(); } void shutdown() throws Exception { client.close(); } enum TestResult {OK, NOK} private void logResult(final TestResult result, final HttpRequest request, final String message) { final StringBuilder buf = new StringBuilder(); buf.append(result); if (buf.length() == 2) { buf.append(" "); } buf.append(": ").append(target); buf.append(": "); buf.append(request.getMethod()).append(" ").append(request.getRequestUri()); if (message != null && !TextUtils.isBlank(message)) { buf.append(" -> ").append(message); } System.out.println(buf); } void execute() { // Initial ping { final HttpCacheContext context = HttpCacheContext.create(); final HttpOptions options = new HttpOptions("*"); try (final ClassicHttpResponse response = client.executeOpen(target, options, context)) { final int code = response.getCode(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server"))); } else { logResult(TestResult.NOK, options, "(status " + code + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, options, "(" + ex.getMessage() + ")"); } } // GET with links { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); final HttpCacheContext context = HttpCacheContext.create(); final Pattern linkPattern = Pattern.compile("^<(.*)>;rel=preload$"); final List links = new ArrayList<>(); final HttpGet getRoot1 = new HttpGet("/"); try (ClassicHttpResponse response = client.executeOpen(target, getRoot1, context)) { final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) { logResult(TestResult.OK, getRoot1, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getRoot1, "(status " + code + ", " + cacheResponseStatus + ")"); } for (final Header header: response.getHeaders("Link")) { final Matcher matcher = linkPattern.matcher(header.getValue()); if (matcher.matches()) { links.add(matcher.group(1)); } } } catch (final Exception ex) { logResult(TestResult.NOK, getRoot1, "(" + ex.getMessage() + ")"); } for (final String link: links) { final HttpGet getLink = new HttpGet(link); try (ClassicHttpResponse response = client.executeOpen(target, getLink, context)) { final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) { logResult(TestResult.OK, getRoot1, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getRoot1, "(status " + code + ", " + cacheResponseStatus + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, getLink, "(" + ex.getMessage() + ")"); } } final HttpGet getRoot2 = new HttpGet("/"); try (ClassicHttpResponse response = client.executeOpen(target, getRoot2, context)) { final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) { logResult(TestResult.OK, getRoot2, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getRoot2, "(status " + code + ", " + cacheResponseStatus + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, getRoot2, "(" + ex.getMessage() + ")"); } for (final String link: links) { final HttpGet getLink = new HttpGet(link); try (ClassicHttpResponse response = client.executeOpen(target, getLink, context)) { final int code = response.getCode(); final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) { logResult(TestResult.OK, getRoot2, "200, " + cacheResponseStatus); } else { logResult(TestResult.NOK, getRoot2, "(status " + code + ", " + cacheResponseStatus + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, getLink, "(" + ex.getMessage() + ")"); } } } } } HttpAsyncClientCompatibilityTest.java000066400000000000000000000436231434266521000441440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.external; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import javax.net.ssl.SSLContext; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; public class HttpAsyncClientCompatibilityTest { public static void main(final String... args) throws Exception { final HttpAsyncClientCompatibilityTest[] tests = new HttpAsyncClientCompatibilityTest[] { new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_1, new HttpHost("http", "localhost", 8080), null, null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_1, new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8888), null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_1, new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8889), new UsernamePasswordCredentials("squid", "nopassword".toCharArray())), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_1, new HttpHost("https", "localhost", 8443), null, null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_1, new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_1, new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889), new UsernamePasswordCredentials("squid", "nopassword".toCharArray())), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_2, new HttpHost("http", "localhost", 8080), null, null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_2, new HttpHost("https", "localhost", 8443), null, null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.NEGOTIATE, new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.NEGOTIATE, new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889), new UsernamePasswordCredentials("squid", "nopassword".toCharArray())), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_2, new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null), new HttpAsyncClientCompatibilityTest( HttpVersionPolicy.FORCE_HTTP_2, new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889), new UsernamePasswordCredentials("squid", "nopassword".toCharArray())) }; for (final HttpAsyncClientCompatibilityTest test: tests) { try { test.execute(); } finally { test.shutdown(); } } } private static final Timeout TIMEOUT = Timeout.ofSeconds(5); private final HttpVersionPolicy versionPolicy; private final HttpHost target; private final HttpHost proxy; private final BasicCredentialsProvider credentialsProvider; private final PoolingAsyncClientConnectionManager connManager; private final CloseableHttpAsyncClient client; HttpAsyncClientCompatibilityTest( final HttpVersionPolicy versionPolicy, final HttpHost target, final HttpHost proxy, final Credentials proxyCreds) throws Exception { this.versionPolicy = versionPolicy; this.target = target; this.proxy = proxy; this.credentialsProvider = new BasicCredentialsProvider(); final RequestConfig requestConfig = RequestConfig.DEFAULT; if (proxy != null && proxyCreds != null) { this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds); } final SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build(); this.connManager = PoolingAsyncClientConnectionManagerBuilder.create() .setTlsStrategy(new DefaultClientTlsStrategy(sslContext)) .setDefaultTlsConfig(TlsConfig.custom() .setVersionPolicy(versionPolicy) .build()) .build(); this.client = HttpAsyncClients.custom() .setConnectionManager(this.connManager) .setProxy(this.proxy) .setDefaultRequestConfig(requestConfig) .build(); } void shutdown() throws Exception { client.close(); } enum TestResult {OK, NOK} private void logResult(final TestResult result, final HttpRequest request, final HttpResponse response, final String message) { final StringBuilder buf = new StringBuilder(); buf.append(result); if (buf.length() == 2) { buf.append(" "); } buf.append(": "); if (response != null) { buf.append(response.getVersion()).append(" "); } else { buf.append(versionPolicy).append(" "); } buf.append(target); if (proxy != null) { buf.append(" via ").append(proxy); } buf.append(": "); buf.append(request.getMethod()).append(" ").append(request.getRequestUri()); if (message != null && !TextUtils.isBlank(message)) { buf.append(" -> ").append(message); } System.out.println(buf); } void execute() throws Exception { client.start(); // Initial ping { final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final SimpleHttpRequest options = SimpleRequestBuilder.options() .setHttpHost(target) .setPath("*") .build(); final Future future = client.execute(options, context, null); try { final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, options, response, Objects.toString(response.getFirstHeader("server"))); } else { logResult(TestResult.NOK, options, response, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, options, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, options, null, "(time out)"); } } // Basic GET requests { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final String[] requestUris = new String[] {"/", "/news.html", "/status.html"}; for (final String requestUri: requestUris) { final SimpleHttpRequest httpGet = SimpleRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(); final Future future = client.execute(httpGet, context, null); try { final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, httpGet, response, "200"); } else { logResult(TestResult.NOK, httpGet, response, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, httpGet, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, httpGet, null, "(time out)"); } } } // Wrong target auth scope { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope("http", "otherhost", -1, "Restricted Files", null), new UsernamePasswordCredentials("testuser", "nopassword".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/private/big-secret.txt") .build(); final Future future = client.execute(httpGetSecret, context, null); try { final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); if (code == HttpStatus.SC_UNAUTHORIZED) { logResult(TestResult.OK, httpGetSecret, response, "401 (wrong target auth scope)"); } else { logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, httpGetSecret, null, "(time out)"); } } // Wrong target credentials { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope(target), new UsernamePasswordCredentials("testuser", "wrong password".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/private/big-secret.txt") .build(); final Future future = client.execute(httpGetSecret, context, null); try { final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); if (code == HttpStatus.SC_UNAUTHORIZED) { logResult(TestResult.OK, httpGetSecret, response, "401 (wrong target creds)"); } else { logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, httpGetSecret, null, "(time out)"); } } // Correct target credentials { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope(target), new UsernamePasswordCredentials("testuser", "nopassword".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/private/big-secret.txt") .build(); final Future future = client.execute(httpGetSecret, context, null); try { final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, httpGetSecret, response, "200 (correct target creds)"); } else { logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, httpGetSecret, null, "(time out)"); } } // Correct target credentials (no keep-alive) if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_1) { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope(target), new UsernamePasswordCredentials("testuser", "nopassword".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/private/big-secret.txt") .build(); httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); final Future future = client.execute(httpGetSecret, context, null); try { final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit()); final int code = response.getCode(); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, httpGetSecret, response, "200 (correct target creds / no keep-alive)"); } else { logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")"); } } catch (final ExecutionException ex) { final Throwable cause = ex.getCause(); logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")"); } catch (final TimeoutException ex) { logResult(TestResult.NOK, httpGetSecret, null, "(time out)"); } } } } HttpClientCompatibilityTest.java000066400000000000000000000312511434266521000431400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/external/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.external; import java.util.Objects; import javax.net.ssl.SSLContext; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpOptions; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.TimeValue; public class HttpClientCompatibilityTest { public static void main(final String... args) throws Exception { final HttpClientCompatibilityTest[] tests = new HttpClientCompatibilityTest[] { new HttpClientCompatibilityTest( new HttpHost("http", "localhost", 8080), null, null), new HttpClientCompatibilityTest( new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8888), null), new HttpClientCompatibilityTest( new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8889), new UsernamePasswordCredentials("squid", "nopassword".toCharArray())), new HttpClientCompatibilityTest( new HttpHost("https", "localhost", 8443), null, null), new HttpClientCompatibilityTest( new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null), new HttpClientCompatibilityTest( new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889), new UsernamePasswordCredentials("squid", "nopassword".toCharArray())) }; for (final HttpClientCompatibilityTest test: tests) { try { test.execute(); } finally { test.shutdown(); } } } private final HttpHost target; private final HttpHost proxy; private final BasicCredentialsProvider credentialsProvider; private final PoolingHttpClientConnectionManager connManager; private final CloseableHttpClient client; HttpClientCompatibilityTest( final HttpHost target, final HttpHost proxy, final Credentials proxyCreds) throws Exception { this.target = target; this.proxy = proxy; this.credentialsProvider = new BasicCredentialsProvider(); final RequestConfig requestConfig = RequestConfig.DEFAULT; if (proxy != null && proxyCreds != null) { this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds); } final SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build(); this.connManager = PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)) .build(); this.client = HttpClients.custom() .setConnectionManager(this.connManager) .setProxy(this.proxy) .setDefaultRequestConfig(requestConfig) .build(); } void shutdown() throws Exception { client.close(); } enum TestResult {OK, NOK} private void logResult(final TestResult result, final HttpRequest request, final String message) { final StringBuilder buf = new StringBuilder(); buf.append(result); if (buf.length() == 2) { buf.append(" "); } buf.append(": ").append(target); if (proxy != null) { buf.append(" via ").append(proxy); } buf.append(": "); buf.append(request.getMethod()).append(" ").append(request.getRequestUri()); if (message != null && !TextUtils.isBlank(message)) { buf.append(" -> ").append(message); } System.out.println(buf); } void execute() { // Initial ping { final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final HttpOptions options = new HttpOptions("*"); try (ClassicHttpResponse response = client.executeOpen(target, options, context)) { final int code = response.getCode(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server"))); } else { logResult(TestResult.NOK, options, "(status " + code + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, options, "(" + ex.getMessage() + ")"); } } // Basic GET requests { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final String[] requestUris = new String[] {"/", "/news.html", "/status.html"}; for (final String requestUri: requestUris) { final HttpGet httpGet = new HttpGet(requestUri); try (ClassicHttpResponse response = client.executeOpen(target, httpGet, context)) { final int code = response.getCode(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, httpGet, "200"); } else { logResult(TestResult.NOK, httpGet, "(status " + code + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, httpGet, "(" + ex.getMessage() + ")"); } } } // Wrong target auth scope { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope("http", "otherhost", -1, "Restricted Files", null), new UsernamePasswordCredentials("testuser", "nopassword".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt"); try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) { final int code = response.getCode(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_UNAUTHORIZED) { logResult(TestResult.OK, httpGetSecret, "401 (wrong target auth scope)"); } else { logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")"); } } // Wrong target credentials { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope(target), new UsernamePasswordCredentials("testuser", "wrong password".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt"); try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) { final int code = response.getCode(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_UNAUTHORIZED) { logResult(TestResult.OK, httpGetSecret, "401 (wrong target creds)"); } else { logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")"); } } // Correct target credentials { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope(target), new UsernamePasswordCredentials("testuser", "nopassword".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt"); try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) { final int code = response.getCode(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, httpGetSecret, "200 (correct target creds)"); } else { logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")"); } } // Correct target credentials (no keep-alive) { connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND); credentialsProvider.setCredentials( new AuthScope(target), new UsernamePasswordCredentials("testuser", "nopassword".toCharArray())); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credentialsProvider); final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt"); httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) { final int code = response.getCode(); EntityUtils.consume(response.getEntity()); if (code == HttpStatus.SC_OK) { logResult(TestResult.OK, httpGetSecret, "200 (correct target creds / no keep-alive)"); } else { logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")"); } } catch (final Exception ex) { logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")"); } } } } fluent/000077500000000000000000000000001434266521000342765ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testingTestFluent.java000066400000000000000000000156461434266521000372520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/fluent/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.fluent; import java.io.File; import java.net.URI; import java.nio.charset.StandardCharsets; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.fluent.Content; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class TestFluent { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public HttpHost targetHost() { return testResources.targetHost(); } @BeforeEach public void setUp() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("/", (request, response, context) -> response.setEntity(new StringEntity("All is well", ContentType.TEXT_PLAIN))); server.registerHandler("/echo", (request, response, context) -> { HttpEntity responseEntity = null; final HttpEntity requestEntity = request.getEntity(); if (requestEntity != null) { final String contentTypeStr = requestEntity.getContentType(); final ContentType contentType = contentTypeStr == null ? ContentType.DEFAULT_TEXT : ContentType.parse(contentTypeStr); if (ContentType.TEXT_PLAIN.getMimeType().equals(contentType.getMimeType())) { responseEntity = new StringEntity( EntityUtils.toString(requestEntity), ContentType.TEXT_PLAIN); } } if (responseEntity == null) { responseEntity = new StringEntity("echo", ContentType.TEXT_PLAIN); } response.setEntity(responseEntity); }); } @Test public void testGetRequest() throws Exception { final HttpHost target = targetHost(); final String baseURL = "http://localhost:" + target.getPort(); final String message = Request.get(baseURL + "/").execute().returnContent().asString(); Assertions.assertEquals("All is well", message); } @Test public void testGetRequestByName() throws Exception { final HttpHost target = targetHost(); final String baseURL = "http://localhost:" + target.getPort(); final String message = Request.create("GET", baseURL + "/").execute().returnContent().asString(); Assertions.assertEquals("All is well", message); } @Test public void testGetRequestByNameWithURI() throws Exception { final HttpHost target = targetHost(); final String baseURL = "http://localhost:" + target.getPort(); final String message = Request.create("GET", new URI(baseURL + "/")).execute().returnContent().asString(); Assertions.assertEquals("All is well", message); } @Test public void testGetRequestFailure() throws Exception { final HttpHost target = targetHost(); final String baseURL = "http://localhost:" + target.getPort(); Assertions.assertThrows(ClientProtocolException.class, () -> Request.get(baseURL + "/boom").execute().returnContent().asString()); } @Test public void testPostRequest() throws Exception { final HttpHost target = targetHost(); final String baseURL = "http://localhost:" + target.getPort(); final String message1 = Request.post(baseURL + "/echo") .bodyString("what is up?", ContentType.TEXT_PLAIN) .execute().returnContent().asString(); Assertions.assertEquals("what is up?", message1); final String message2 = Request.post(baseURL + "/echo") .bodyByteArray(new byte[]{1, 2, 3}, ContentType.APPLICATION_OCTET_STREAM) .execute().returnContent().asString(); Assertions.assertEquals("echo", message2); } @Test public void testContentAsStringWithCharset() throws Exception { final HttpHost target = targetHost(); final String baseURL = "http://localhost:" + target.getPort(); final Content content = Request.post(baseURL + "/echo").bodyByteArray("Ü".getBytes(StandardCharsets.UTF_8)).execute() .returnContent(); Assertions.assertEquals((byte)-61, content.asBytes()[0]); Assertions.assertEquals((byte)-100, content.asBytes()[1]); Assertions.assertEquals("Ü", content.asString(StandardCharsets.UTF_8)); } @Test public void testConnectionRelease() throws Exception { final HttpHost target = targetHost(); final String baseURL = "http://localhost:" + target.getPort(); for (int i = 0; i < 20; i++) { Request.get(baseURL + "/").execute().returnContent(); Request.get(baseURL + "/").execute().returnResponse(); Request.get(baseURL + "/").execute().discardContent(); Request.get(baseURL + "/").execute().handleResponse(response -> null); final File tmpFile = File.createTempFile("test", ".bin"); try { Request.get(baseURL + "/").execute().saveContent(tmpFile); } finally { tmpFile.delete(); } } } } sync/000077500000000000000000000000001434266521000337555ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testingTestBasicConnectionManager.java000066400000000000000000000071021434266521000420140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager; import org.apache.hc.client5.testing.classic.RandomHandler; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class TestBasicConnectionManager { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); @Test public void testBasics() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = testResources.targetHost(); final CloseableHttpClient client = testResources.startClient(builder -> builder .setConnectionManager(new BasicHttpClientConnectionManager()) ); final HttpGet get = new HttpGet("/random/1024"); client.execute(target, get, response -> { Assertions.assertEquals(200, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); } @Test public void testConnectionStillInUse() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = testResources.targetHost(); final CloseableHttpClient client = testResources.startClient(builder -> builder .setConnectionManager(new BasicHttpClientConnectionManager()) ); final HttpGet get1 = new HttpGet("/random/1024"); client.executeOpen(target, get1, null); final HttpGet get2 = new HttpGet("/random/1024"); Assertions.assertThrows(IllegalStateException.class, () -> client.executeOpen(target, get2, null)); } } TestClientAuthentication.java000066400000000000000000001023341434266521000416010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import static org.hamcrest.MatcherAssert.assertThat; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.stream.Collectors; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpPut; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.impl.auth.BasicAuthCache; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.BasicTestAuthenticator; import org.apache.hc.client5.testing.auth.Authenticator; import org.apache.hc.client5.testing.classic.AuthenticatingDecorator; import org.apache.hc.client5.testing.classic.EchoHandler; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.support.BasicResponseBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mockito; /** * Unit tests for automatic client authentication. */ public class TestClientAuthentication { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public ClassicTestServer startServer(final Authenticator authenticator) throws IOException { return testResources.startServer( null, null, requestHandler -> new AuthenticatingDecorator(requestHandler, authenticator)); } public ClassicTestServer startServer() throws IOException { return startServer(new BasicTestAuthenticator("test:test", "test realm")); } public CloseableHttpClient startClient(final Consumer clientCustomizer) { return testResources.startClient(clientCustomizer); } public CloseableHttpClient startClient() { return testResources.startClient(builder -> {}); } public HttpHost targetHost() { return testResources.targetHost(); } @Test public void testBasicAuthenticationNoCreds() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); context.setCredentialsProvider(credsProvider); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationFailure() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "all-wrong".toCharArray())); context.setCredentialsProvider(credsProvider); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationSuccess() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet httpget = new HttpGet("/"); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); context.setCredentialsProvider(credsProvider); client.execute(target, httpget, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationSuccessOnNonRepeatablePutExpectContinue() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final RequestConfig config = RequestConfig.custom() .setExpectContinueEnabled(true) .build(); final HttpPut httpput = new HttpPut("/"); httpput.setConfig(config); httpput.setEntity(new InputStreamEntity( new ByteArrayInputStream( new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ), -1, null)); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); context.setCredentialsProvider(credsProvider); client.execute(target, httpput, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity); return null; }); } @Test public void testBasicAuthenticationFailureOnNonRepeatablePutDontExpectContinue() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(false).build(); final HttpPut httpput = new HttpPut("/"); httpput.setConfig(config); httpput.setEntity(new InputStreamEntity( new ByteArrayInputStream( new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ), -1, null)); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "boom".toCharArray())); context.setCredentialsProvider(credsProvider); client.execute(target, httpput, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(401, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); } @Test public void testBasicAuthenticationSuccessOnRepeatablePost() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpPost httppost = new HttpPost("/"); httppost.setEntity(new StringEntity("some important stuff", StandardCharsets.US_ASCII)); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); context.setCredentialsProvider(credsProvider); client.execute(target, httppost, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } @Test public void testBasicAuthenticationFailureOnNonRepeatablePost() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpPost httppost = new HttpPost("/"); httppost.setEntity(new InputStreamEntity( new ByteArrayInputStream( new byte[] { 0,1,2,3,4,5,6,7,8,9 }), -1, null)); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom() .setExpectContinueEnabled(false) .build()); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); context.setCredentialsProvider(credsProvider); client.execute(target, httppost, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(401, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); } @Test public void testBasicAuthenticationCredentialsCaching() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy()); final Queue responseQueue = new ConcurrentLinkedQueue<>(); final CloseableHttpClient client = startClient(builder -> builder .setTargetAuthenticationStrategy(authStrategy) .addResponseInterceptorLast((response, entity, context) -> responseQueue.add(BasicResponseBuilder.copy(response).build()))); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(target, "test", "test".toCharArray()) .build()); for (int i = 0; i < 5; i++) { final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { final HttpEntity entity1 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity1); EntityUtils.consume(entity1); return null; }); } Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any()); assertThat( responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()), CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200, 200))); } @Test public void testBasicAuthenticationCredentialsCachingByPathPrefix() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy()); final Queue responseQueue = new ConcurrentLinkedQueue<>(); final CloseableHttpClient client = startClient(builder -> builder .setTargetAuthenticationStrategy(authStrategy) .addResponseInterceptorLast((response, entity, context) -> responseQueue.add(BasicResponseBuilder.copy(response).build()))); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(target, "test", "test".toCharArray()) .build(); final AuthCache authCache = new BasicAuthCache(); final HttpClientContext context = HttpClientContext.create(); context.setAuthCache(authCache); context.setCredentialsProvider(credentialsProvider); for (final String requestPath: new String[] {"/blah/a", "/blah/b?huh", "/blah/c", "/bl%61h/%61"}) { final HttpGet httpget = new HttpGet(requestPath); client.execute(target, httpget, context, response -> { final HttpEntity entity1 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity1); EntityUtils.consume(entity1); return null; }); } // There should be only single auth strategy call for all successful message exchanges Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any()); assertThat( responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()), CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200))); responseQueue.clear(); authCache.clear(); Mockito.reset(authStrategy); for (final String requestPath: new String[] {"/blah/a", "/yada/a", "/blah/blah/", "/buh/a"}) { final HttpGet httpget = new HttpGet(requestPath); client.execute(target, httpget, context, response -> { final HttpEntity entity1 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity1); EntityUtils.consume(entity1); return null; }); } // There should be an auth strategy call for all successful message exchanges Mockito.verify(authStrategy, Mockito.times(2)).select(Mockito.any(), Mockito.any(), Mockito.any()); assertThat( responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()), CoreMatchers.equalTo(Arrays.asList(200, 401, 200, 200, 401, 200))); } @Test public void testAuthenticationCredentialsCachingReauthenticationOnDifferentRealm() throws Exception { final ClassicTestServer server = startServer(new Authenticator() { @Override public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) { if (requestUri.equals("/this")) { return "test:this".equals(credentials); } else if (requestUri.equals("/that")) { return "test:that".equals(credentials); } else { return "test:test".equals(credentials); } } @Override public String getRealm(final URIAuthority authority, final String requestUri) { if (requestUri.equals("/this")) { return "this realm"; } else if (requestUri.equals("/that")) { return "that realm"; } else { return "test realm"; } } }); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy()); final CloseableHttpClient client = startClient(builder -> builder .setTargetAuthenticationStrategy(authStrategy) ); final CredentialsProvider credsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(target, "this realm", null), "test", "this".toCharArray()) .add(new AuthScope(target, "that realm", null), "test", "that".toCharArray()) .build(); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); final HttpGet httpget1 = new HttpGet("/this"); client.execute(target, httpget1, context, response -> { final HttpEntity entity1 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity1); EntityUtils.consume(entity1); return null; }); final HttpGet httpget2 = new HttpGet("/this"); client.execute(target, httpget2, context, response -> { final HttpEntity entity2 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity2); EntityUtils.consume(entity2); return null; }); final HttpGet httpget3 = new HttpGet("/that"); client.execute(target, httpget3, context, response -> { final HttpEntity entity3 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity3); EntityUtils.consume(entity3); return null; }); Mockito.verify(authStrategy, Mockito.times(2)).select(Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testAuthenticationUserinfoInRequest() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet httpget = new HttpGet("http://test:test@" + target.toHostString() + "/"); final HttpClientContext context = HttpClientContext.create(); Assertions.assertThrows(ClientProtocolException.class, () -> client.execute(target, httpget, context, response -> null)); } @Test public void testPreemptiveAuthentication() throws Exception { final Authenticator authenticator = Mockito.spy(new BasicTestAuthenticator("test:test", "test realm")); final ClassicTestServer server = startServer(authenticator); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final BasicScheme basicScheme = new BasicScheme(); basicScheme.initPreemptive(new UsernamePasswordCredentials("test", "test".toCharArray())); final HttpClientContext context = HttpClientContext.create(); final AuthCache authCache = new BasicAuthCache(); authCache.put(target, basicScheme); context.setAuthCache(authCache); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { final HttpEntity entity1 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity1); EntityUtils.consume(entity1); return null; }); Mockito.verify(authenticator).authenticate(Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testPreemptiveAuthenticationFailure() throws Exception { final Authenticator authenticator = Mockito.spy(new BasicTestAuthenticator("test:test", "test realm")); final ClassicTestServer server = startServer(authenticator); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final AuthCache authCache = new BasicAuthCache(); authCache.put(target, new BasicScheme()); context.setAuthCache(authCache); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(target, "test", "stuff".toCharArray()) .build()); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { final HttpEntity entity1 = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); Assertions.assertNotNull(entity1); EntityUtils.consume(entity1); return null; }); Mockito.verify(authenticator).authenticate(Mockito.any(), Mockito.any(), Mockito.any()); } static class ProxyAuthHandler implements HttpRequestHandler { @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { final String creds = (String) context.getAttribute("creds"); if (creds == null || !creds.equals("test:test")) { response.setCode(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED); } else { response.setCode(HttpStatus.SC_OK); final StringEntity entity = new StringEntity("success", StandardCharsets.US_ASCII); response.setEntity(entity); } } } @Test public void testAuthenticationTargetAsProxy() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("*", new ProxyAuthHandler()); final HttpHost target = testResources.targetHost(); final CloseableHttpClient client = testResources.startClient(builder -> {}); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); context.setCredentialsProvider(credsProvider); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, response.getCode()); EntityUtils.consume(entity); return null; }); } @Test public void testConnectionCloseAfterAuthenticationSuccess() throws Exception { final ClassicTestServer server = testResources.startServer( Http1Config.DEFAULT, HttpProcessors.server(), requestHandler -> new AuthenticatingDecorator(requestHandler, new BasicTestAuthenticator("test:test", "test realm")) { @Override protected void customizeUnauthorizedResponse(final ClassicHttpResponse unauthorized) { unauthorized.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } } ); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = CredentialsProviderBuilder.create() .add(target, "test", "test".toCharArray()) .build(); context.setCredentialsProvider(credsProvider); for (int i = 0; i < 2; i++) { final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); return null; }); } } @Test public void testReauthentication() throws Exception { final BasicSchemeFactory myBasicAuthSchemeFactory = new BasicSchemeFactory() { @Override public AuthScheme create(final HttpContext context) { return new BasicScheme() { private static final long serialVersionUID = 1L; @Override public String getName() { return "MyBasic"; } }; } }; final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); final RequestConfig config = RequestConfig.custom() .setTargetPreferredAuthSchemes(Collections.singletonList("MyBasic")) .build(); final Registry authSchemeRegistry = RegistryBuilder.create() .register("MyBasic", myBasicAuthSchemeFactory) .build(); final Authenticator authenticator = new BasicTestAuthenticator("test:test", "test realm") { private final AtomicLong count = new AtomicLong(0); @Override public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) { final boolean authenticated = super.authenticate(authority, requestUri, credentials); if (authenticated) { return this.count.incrementAndGet() % 4 != 0; } return false; } }; final ClassicTestServer server = testResources.startServer( Http1Config.DEFAULT, HttpProcessors.server(), requestHandler -> new AuthenticatingDecorator(requestHandler, authenticator) { @Override protected void customizeUnauthorizedResponse(final ClassicHttpResponse unauthorized) { unauthorized.removeHeaders(HttpHeaders.WWW_AUTHENTICATE); unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\""); } } ); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(builder -> builder .setDefaultAuthSchemeRegistry(authSchemeRegistry) .setDefaultCredentialsProvider(credsProvider) ); final HttpClientContext context = HttpClientContext.create(); for (int i = 0; i < 10; i++) { final HttpGet httpget = new HttpGet("/"); httpget.setConfig(config); client.execute(target, httpget, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); } } @Test public void testAuthenticationFallback() throws Exception { final ClassicTestServer server = testResources.startServer( Http1Config.DEFAULT, HttpProcessors.server(), requestHandler -> new AuthenticatingDecorator(requestHandler, new BasicTestAuthenticator("test:test", "test realm")) { @Override protected void customizeUnauthorizedResponse(final ClassicHttpResponse unauthorized) { unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"test realm\" invalid"); } } ); server.registerHandler("*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final CredentialsProvider credsProvider = Mockito.mock(CredentialsProvider.class); Mockito.when(credsProvider.getCredentials(Mockito.any(), Mockito.any())) .thenReturn(new UsernamePasswordCredentials("test", "test".toCharArray())); context.setCredentialsProvider(credsProvider); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { final HttpEntity entity = response.getEntity(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); Assertions.assertNotNull(entity); EntityUtils.consume(entity); return null; }); Mockito.verify(credsProvider).getCredentials( Mockito.eq(new AuthScope(target, "test realm", "basic")), Mockito.any()); } } TestClientAuthenticationFakeNTLM.java000066400000000000000000000252311434266521000430630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.IOException; import java.util.function.Consumer; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.NTCredentials; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Unit tests for some of the NTLM auth functionality.. */ public class TestClientAuthenticationFakeNTLM { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public ClassicTestServer startServer() throws IOException { return testResources.startServer(null, null, null); } public CloseableHttpClient startClient(final Consumer clientCustomizer) { return testResources.startClient(clientCustomizer); } public CloseableHttpClient startClient() { return testResources.startClient(builder -> {}); } public HttpHost targetHost() { return testResources.targetHost(); } static class NtlmResponseHandler implements HttpRequestHandler { @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_UNAUTHORIZED); response.setHeader("Connection", "Keep-Alive"); response.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.NTLM); } } @Test public void testNTLMAuthenticationFailure() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new NtlmResponseHandler()); final HttpHost target = targetHost(); final CredentialsProvider credsProvider = CredentialsProviderBuilder.create() .add(target, new NTCredentials("test", "test".toCharArray(), null, null)) .build(); final CloseableHttpClient client = startClient(builder -> builder .setDefaultCredentialsProvider(credsProvider) ); final HttpContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); return null; }); } static class NtlmType2MessageResponseHandler implements HttpRequestHandler { private final String authenticateHeaderValue; public NtlmType2MessageResponseHandler(final String type2Message) { this.authenticateHeaderValue = StandardAuthScheme.NTLM + " " + type2Message; } @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_UNAUTHORIZED); response.setHeader("Connection", "Keep-Alive"); if (!request.containsHeader(HttpHeaders.AUTHORIZATION)) { response.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.NTLM); } else { response.setHeader(HttpHeaders.WWW_AUTHENTICATE, authenticateHeaderValue); } } } @Test public void testNTLMv1Type2Message() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new NtlmType2MessageResponseHandler("TlRMTVNTUAACAA" + "AADAAMADgAAAAzggLiASNFZ4mrze8AAAAAAAAAAAAAAAAAAAAABgBwFwAAAA9T" + "AGUAcgB2AGUAcgA=")); final HttpHost target = targetHost(); final CredentialsProvider credsProvider = CredentialsProviderBuilder.create() .add(target, new NTCredentials("test", "test".toCharArray(), null, null)) .build(); final CloseableHttpClient client = startClient(builder -> builder .setDefaultCredentialsProvider(credsProvider) ); final HttpContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); return null; }); } @Test public void testNTLMv2Type2Message() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new NtlmType2MessageResponseHandler("TlRMTVNTUAACAA" + "AADAAMADgAAAAzgoriASNFZ4mrze8AAAAAAAAAACQAJABEAAAABgBwFwAAAA9T" + "AGUAcgB2AGUAcgACAAwARABvAG0AYQBpAG4AAQAMAFMAZQByAHYAZQByAAAAAAA=")); final HttpHost target = targetHost(); final CredentialsProvider credsProvider = CredentialsProviderBuilder.create() .add(target, new NTCredentials("test", "test".toCharArray(), null, null)) .build(); final CloseableHttpClient client = startClient(builder -> builder .setDefaultCredentialsProvider(credsProvider) ); final HttpContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); return null; }); } static class NtlmType2MessageOnlyResponseHandler implements HttpRequestHandler { private final String authenticateHeaderValue; public NtlmType2MessageOnlyResponseHandler(final String type2Message) { this.authenticateHeaderValue = StandardAuthScheme.NTLM + " " + type2Message; } @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_UNAUTHORIZED); response.setHeader("Connection", "Keep-Alive"); response.setHeader(HttpHeaders.WWW_AUTHENTICATE, authenticateHeaderValue); } } @Test public void testNTLMType2MessageOnlyAuthenticationFailure() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new NtlmType2MessageOnlyResponseHandler("TlRMTVNTUAACAA" + "AADAAMADgAAAAzggLiASNFZ4mrze8AAAAAAAAAAAAAAAAAAAAABgBwFwAAAA9T" + "AGUAcgB2AGUAcgA=")); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(target, new NTCredentials("test", "test".toCharArray(), null, null)) .build()); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); return null; }); } @Test public void testNTLMType2NonUnicodeMessageOnlyAuthenticationFailure() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new NtlmType2MessageOnlyResponseHandler("TlRMTVNTUAACAA" + "AABgAGADgAAAAyggLiASNFZ4mrze8AAAAAAAAAAAAAAAAAAAAABgBwFwAAAA9T" + "ZXJ2ZXI=")); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(target, new NTCredentials("test", "test".toCharArray(), null, null)) .build()); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); return null; }); } } TestClientRequestExecution.java000066400000000000000000000341531434266521000421410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.RedirectLocations; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Client protocol handling tests. */ public class TestClientRequestExecution { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public ClassicTestServer startServer() throws IOException { return testResources.startServer(null, null, null); } public CloseableHttpClient startClient(final Consumer clientCustomizer) { return testResources.startClient(clientCustomizer); } public CloseableHttpClient startClient() { return testResources.startClient(builder -> {}); } public HttpHost targetHost() { return testResources.targetHost(); } private static class SimpleService implements HttpRequestHandler { public SimpleService() { super(); } @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_OK); final StringEntity entity = new StringEntity("Whatever"); response.setEntity(entity); } } private static class FaultyHttpRequestExecutor extends HttpRequestExecutor { private static final String MARKER = "marker"; private final String failureMsg; public FaultyHttpRequestExecutor(final String failureMsg) { this.failureMsg = failureMsg; } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final HttpClientConnection conn, final HttpContext context) throws IOException, HttpException { final ClassicHttpResponse response = super.execute(request, conn, context); final Object marker = context.getAttribute(MARKER); if (marker == null) { context.setAttribute(MARKER, Boolean.TRUE); throw new IOException(failureMsg); } return response; } } @Test public void testAutoGeneratedHeaders() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new SimpleService()); final HttpHost target = targetHost(); final HttpRequestInterceptor interceptor = (request, entityDetails, context) -> request.addHeader("my-header", "stuff"); final HttpRequestRetryStrategy requestRetryStrategy = new HttpRequestRetryStrategy() { @Override public boolean retryRequest( final HttpRequest request, final IOException exception, final int executionCount, final HttpContext context) { return true; } @Override public boolean retryRequest( final HttpResponse response, final int executionCount, final HttpContext context) { return false; } @Override public TimeValue getRetryInterval( final HttpResponse response, final int executionCount, final HttpContext context) { return TimeValue.ofSeconds(1L); } }; final CloseableHttpClient client = startClient(builder -> builder .addRequestInterceptorFirst(interceptor) .setRequestExecutor(new FaultyHttpRequestExecutor("Oppsie")) .setRetryStrategy(requestRetryStrategy) ); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/"); client.execute(target, httpget, context, response -> { EntityUtils.consume(response.getEntity()); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); final Header[] myheaders = reqWrapper.getHeaders("my-header"); Assertions.assertNotNull(myheaders); Assertions.assertEquals(1, myheaders.length); return null; }); } @Test public void testNonRepeatableEntity() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new SimpleService()); final HttpHost target = targetHost(); final HttpRequestRetryStrategy requestRetryStrategy = new HttpRequestRetryStrategy() { @Override public boolean retryRequest( final HttpRequest request, final IOException exception, final int executionCount, final HttpContext context) { return true; } @Override public boolean retryRequest( final HttpResponse response, final int executionCount, final HttpContext context) { return false; } @Override public TimeValue getRetryInterval( final HttpResponse response, final int executionCount, final HttpContext context) { return TimeValue.ofSeconds(1L); } }; final CloseableHttpClient client = startClient(builder -> builder .setRequestExecutor(new FaultyHttpRequestExecutor("a message showing that this failed")) .setRetryStrategy(requestRetryStrategy) ); final HttpClientContext context = HttpClientContext.create(); final HttpPost httppost = new HttpPost("/"); httppost.setEntity(new InputStreamEntity( new ByteArrayInputStream( new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ), -1, null)); Assertions.assertThrows(IOException.class, () -> client.execute(target, httppost, context, response -> null)); } @Test public void testNonCompliantURI() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new SimpleService()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", "{{|boom|}}"); client.execute(target, request, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals("{{|boom|}}", reqWrapper.getRequestUri()); } @Test public void testRelativeRequestURIWithFragment() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new SimpleService()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet httpget = new HttpGet("/stuff#blahblah"); final HttpClientContext context = HttpClientContext.create(); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest request = context.getRequest(); Assertions.assertEquals("/stuff", request.getRequestUri()); } @Test public void testAbsoluteRequestURIWithFragment() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new SimpleService()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final URI uri = new URIBuilder() .setHost(target.getHostName()) .setPort(target.getPort()) .setScheme(target.getSchemeName()) .setPath("/stuff") .setFragment("blahblah") .build(); final HttpGet httpget = new HttpGet(uri); final HttpClientContext context = HttpClientContext.create(); client.execute(httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); return null; }); final HttpRequest request = context.getRequest(); Assertions.assertEquals("/stuff", request.getRequestUri()); final RedirectLocations redirectLocations = context.getRedirectLocations(); final URI location = URIUtils.resolve(uri, target, redirectLocations.getAll()); Assertions.assertEquals(uri, location); } @Test @Disabled("Fails intermittently with GitHub Actions") public void testRequestCancellation() throws Exception { startServer(); final HttpHost target = targetHost(); final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnPerRoute(1) .setMaxConnTotal(1), builder -> {}); final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); try { for (int i = 0; i < 20; i++) { final HttpGet httpget = new HttpGet("/random/1000"); executorService.schedule(httpget::cancel, 1, TimeUnit.MILLISECONDS); try { client.execute(target, httpget, response -> { EntityUtils.consume(response.getEntity()); return null; }); } catch (final Exception ignore) { } } final Random rnd = new Random(); for (int i = 0; i < 20; i++) { final HttpGet httpget = new HttpGet("/random/1000"); executorService.schedule(httpget::cancel, rnd.nextInt(200), TimeUnit.MILLISECONDS); try { client.execute(target, httpget, response -> { EntityUtils.consume(response.getEntity()); return null; }); } catch (final Exception ignore) { } } for (int i = 0; i < 5; i++) { final HttpGet httpget = new HttpGet("/random/1000"); client.execute(target, httpget, response -> { EntityUtils.consume(response.getEntity()); return null; }); } } finally { executorService.shutdownNow(); } } } TestConnectionManagement.java000066400000000000000000000340221434266521000415550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.IOException; import java.util.concurrent.TimeoutException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.testing.classic.RandomHandler; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestConnControl; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Tests for {@code PoolingHttpClientConnectionManager} that do require a server * to communicate with. */ public class TestConnectionManagement { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public ClassicTestServer startServer() throws IOException { return testResources.startServer(null, null, null); } public CloseableHttpClient startClient() { return testResources.startClient(b -> {}, b -> {}); } public HttpHost targetHost() { return testResources.targetHost(); } /** * Tests releasing and re-using a connection after a response is read. */ @Test public void testReleaseConnection() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); startClient(); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); connManager.setMaxTotal(1); final HttpRoute route = new HttpRoute(target, null, false); final int rsplen = 8; final String uri = "/random/" + rsplen; final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", target, uri); final HttpContext context = new BasicHttpContext(); final LeaseRequest leaseRequest1 = connManager.lease("id1", route, null); final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS); connManager.connect(endpoint1, null, context); final HttpProcessor httpProcessor = new DefaultHttpProcessor( new RequestTargetHost(), new RequestContent(), new RequestConnControl()); final HttpRequestExecutor exec = new HttpRequestExecutor(); exec.preProcess(request, httpProcessor, context); try (final ClassicHttpResponse response1 = endpoint1.execute("id1", request, exec, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response1.getCode()); } // check that there is no auto-release by default // this should fail quickly, connection has not been released final LeaseRequest leaseRequest2 = connManager.lease("id2", route, null); Assertions.assertThrows(TimeoutException.class, () -> leaseRequest2.get(Timeout.ofMilliseconds(10))); endpoint1.close(); connManager.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND); final LeaseRequest leaseRequest3 = connManager.lease("id2", route, null); final ConnectionEndpoint endpoint2 = leaseRequest3.get(Timeout.ZERO_MILLISECONDS); Assertions.assertFalse(endpoint2.isConnected()); connManager.connect(endpoint2, null, context); try (final ClassicHttpResponse response2 = endpoint2.execute("id2", request, exec, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response2.getCode()); } // release connection after marking it for re-use // expect the next connection obtained to be open connManager.release(endpoint2, null, TimeValue.NEG_ONE_MILLISECOND); final LeaseRequest leaseRequest4 = connManager.lease("id3", route, null); final ConnectionEndpoint endpoint3 = leaseRequest4.get(Timeout.ZERO_MILLISECONDS); Assertions.assertTrue(endpoint3.isConnected()); // repeat the communication, no need to prepare the request again try (final ClassicHttpResponse response3 = endpoint3.execute("id3", request, exec, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response3.getCode()); } connManager.release(endpoint3, null, TimeValue.NEG_ONE_MILLISECOND); connManager.close(); } /** * Tests releasing with time limits. */ @Test public void testReleaseConnectionWithTimeLimits() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); startClient(); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); connManager.setMaxTotal(1); final HttpRoute route = new HttpRoute(target, null, false); final int rsplen = 8; final String uri = "/random/" + rsplen; final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", target, uri); final HttpContext context = new BasicHttpContext(); final LeaseRequest leaseRequest1 = connManager.lease("id1", route, null); final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS); connManager.connect(endpoint1, null, context); final HttpProcessor httpProcessor = new DefaultHttpProcessor( new RequestTargetHost(), new RequestContent(), new RequestConnControl()); final HttpRequestExecutor exec = new HttpRequestExecutor(); exec.preProcess(request, httpProcessor, context); try (final ClassicHttpResponse response1 = endpoint1.execute("id1", request, exec, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response1.getCode()); } // check that there is no auto-release by default final LeaseRequest leaseRequest2 = connManager.lease("id2", route, null); // this should fail quickly, connection has not been released Assertions.assertThrows(TimeoutException.class, () -> leaseRequest2.get(Timeout.ofMilliseconds(10))); endpoint1.close(); connManager.release(endpoint1, null, TimeValue.ofMilliseconds(100)); final LeaseRequest leaseRequest3 = connManager.lease("id2", route, null); final ConnectionEndpoint endpoint2 = leaseRequest3.get(Timeout.ZERO_MILLISECONDS); Assertions.assertFalse(endpoint2.isConnected()); connManager.connect(endpoint2, null, context); try (final ClassicHttpResponse response2 = endpoint2.execute("id2", request, exec, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response2.getCode()); } connManager.release(endpoint2, null, TimeValue.ofMilliseconds(100)); final LeaseRequest leaseRequest4 = connManager.lease("id3", route, null); final ConnectionEndpoint endpoint3 = leaseRequest4.get(Timeout.ZERO_MILLISECONDS); Assertions.assertTrue(endpoint3.isConnected()); // repeat the communication, no need to prepare the request again try (final ClassicHttpResponse response3 = endpoint3.execute("id3", request, exec, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response3.getCode()); } connManager.release(endpoint3, null, TimeValue.ofMilliseconds(100)); Thread.sleep(150); final LeaseRequest leaseRequest5 = connManager.lease("id4", route, null); final ConnectionEndpoint endpoint4 = leaseRequest5.get(Timeout.ZERO_MILLISECONDS); Assertions.assertFalse(endpoint4.isConnected()); // repeat the communication, no need to prepare the request again connManager.connect(endpoint4, null, context); try (final ClassicHttpResponse response4 = endpoint4.execute("id4", request, exec, context)) { Assertions.assertEquals(HttpStatus.SC_OK, response4.getCode()); } connManager.close(); } @Test public void testCloseExpiredIdleConnections() throws Exception { startServer(); final HttpHost target = targetHost(); startClient(); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); connManager.setMaxTotal(1); final HttpRoute route = new HttpRoute(target, null, false); final HttpContext context = new BasicHttpContext(); final LeaseRequest leaseRequest1 = connManager.lease("id1", route, null); final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS); connManager.connect(endpoint1, null, context); Assertions.assertEquals(1, connManager.getTotalStats().getLeased()); Assertions.assertEquals(1, connManager.getStats(route).getLeased()); connManager.release(endpoint1, null, TimeValue.ofMilliseconds(100)); // Released, still active. Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); Assertions.assertEquals(1, connManager.getStats(route).getAvailable()); connManager.closeExpired(); // Time has not expired yet. Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); Assertions.assertEquals(1, connManager.getStats(route).getAvailable()); Thread.sleep(150); connManager.closeExpired(); // Time expired now, connections are destroyed. Assertions.assertEquals(0, connManager.getTotalStats().getAvailable()); Assertions.assertEquals(0, connManager.getStats(route).getAvailable()); connManager.close(); } @Test public void testCloseExpiredTTLConnections() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); testResources.startClient( builder -> builder .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT) .setConnPoolPolicy(PoolReusePolicy.LIFO) .setDefaultConnectionConfig(ConnectionConfig.custom() .setTimeToLive(TimeValue.ofMilliseconds(100)) .build()) .setMaxConnTotal(1), builder -> {} ); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); connManager.setMaxTotal(1); final HttpRoute route = new HttpRoute(target, null, false); final HttpContext context = new BasicHttpContext(); final LeaseRequest leaseRequest1 = connManager.lease("id1", route, null); final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS); connManager.connect(endpoint1, null, context); Assertions.assertEquals(1, connManager.getTotalStats().getLeased()); Assertions.assertEquals(1, connManager.getStats(route).getLeased()); // Release, let remain idle for forever connManager.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND); // Released, still active. Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); Assertions.assertEquals(1, connManager.getStats(route).getAvailable()); connManager.closeExpired(); // Time has not expired yet. Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); Assertions.assertEquals(1, connManager.getStats(route).getAvailable()); Thread.sleep(150); connManager.closeExpired(); // TTL expired now, connections are destroyed. Assertions.assertEquals(0, connManager.getTotalStats().getAvailable()); Assertions.assertEquals(0, connManager.getStats(route).getAvailable()); connManager.close(); } } TestConnectionReuse.java000066400000000000000000000351131434266521000405660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.testing.classic.RandomHandler; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.HttpProcessors; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class TestConnectionReuse { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public HttpHost targetHost() { return testResources.targetHost(); } @Test public void testReuseOfPersistentConnections() throws Exception { final ClassicTestServer server = testResources.startServer( null, null, null); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnTotal(5) .setMaxConnPerRoute(5), builder -> { } ); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); final WorkerThread[] workers = new WorkerThread[10]; for (int i = 0; i < workers.length; i++) { workers[i] = new WorkerThread( client, target, new URI("/random/2000"), 10, false); } for (final WorkerThread worker : workers) { worker.start(); } for (final WorkerThread worker : workers) { worker.join(10000); final Exception ex = worker.getException(); if (ex != null) { throw ex; } } // Expect leased connections to be returned Assertions.assertEquals(0, connManager.getTotalStats().getLeased()); // Expect some connection in the pool Assertions.assertTrue(connManager.getTotalStats().getAvailable() > 0); } @Test public void testReuseOfPersistentConnectionsWithStreamedRequestAndResponse() throws Exception { final ClassicTestServer server = testResources.startServer( null, null, null); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnTotal(5) .setMaxConnPerRoute(5), builder -> { } ); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); final WorkerThread[] workers = new WorkerThread[10]; for (int i = 0; i < workers.length; i++) { final List requests = new ArrayList<>(); for (int j = 0; j < 10; j++) { final HttpPost post = new HttpPost(new URI("/random/2000")); // non-repeatable post.setEntity(new InputStreamEntity( new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8)), ContentType.APPLICATION_OCTET_STREAM)); requests.add(post); } workers[i] = new WorkerThread(client, target, false, requests); } for (final WorkerThread worker : workers) { worker.start(); } for (final WorkerThread worker : workers) { worker.join(10000); final Exception ex = worker.getException(); if (ex != null) { throw ex; } } // Expect leased connections to be returned Assertions.assertEquals(0, connManager.getTotalStats().getLeased()); // Expect some connection in the pool Assertions.assertTrue(connManager.getTotalStats().getAvailable() > 0); } private static class AlwaysCloseConn implements HttpResponseInterceptor { @Override public void process( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { response.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); } } @Test public void testReuseOfClosedConnections() throws Exception { final HttpProcessor httpproc = HttpProcessors.customServer(null) .add(new AlwaysCloseConn()) .build(); final ClassicTestServer server = testResources.startServer( null, httpproc, null); final HttpHost target = targetHost(); final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnTotal(5) .setMaxConnPerRoute(5), builder -> { } ); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); final WorkerThread[] workers = new WorkerThread[10]; for (int i = 0; i < workers.length; i++) { workers[i] = new WorkerThread( client, target, new URI("/random/2000"), 10, false); } for (final WorkerThread worker : workers) { worker.start(); } for (final WorkerThread worker : workers) { worker.join(10000); final Exception ex = worker.getException(); if (ex != null) { throw ex; } } // Expect leased connections to be returned Assertions.assertEquals(0, connManager.getTotalStats().getLeased()); // Expect zero connections in the pool Assertions.assertEquals(0, connManager.getTotalStats().getAvailable()); } @Test public void testReuseOfAbortedConnections() throws Exception { final ClassicTestServer server = testResources.startServer( null, null, null); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnTotal(5) .setMaxConnPerRoute(5), builder -> { } ); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); final WorkerThread[] workers = new WorkerThread[10]; for (int i = 0; i < workers.length; i++) { workers[i] = new WorkerThread( client, target, new URI("/random/2000"), 10, true); } for (final WorkerThread worker : workers) { worker.start(); } for (final WorkerThread worker : workers) { worker.join(10000); final Exception ex = worker.getException(); if (ex != null) { throw ex; } } // Expect leased connections to be returned Assertions.assertEquals(0, connManager.getTotalStats().getLeased()); // Expect some connections in the pool Assertions.assertTrue(connManager.getTotalStats().getAvailable() > 0); } @Test public void testKeepAliveHeaderRespected() throws Exception { final HttpProcessor httpproc = HttpProcessors.customServer(null) .add(new ResponseKeepAlive()) .build(); final ClassicTestServer server = testResources.startServer( null, httpproc, null); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnTotal(1) .setMaxConnPerRoute(1), builder -> { } ); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); client.execute(target, new HttpGet("/random/2000"), response -> { EntityUtils.consume(response.getEntity()); return null; }); Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); client.execute(target, new HttpGet("/random/2000"), response -> { EntityUtils.consume(response.getEntity()); return null; }); Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); // Now sleep for 1.1 seconds and let the timeout do its work Thread.sleep(1100); client.execute(target, new HttpGet("/random/2000"), response -> { EntityUtils.consume(response.getEntity()); return null; }); Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); // Do another request just under the 1 second limit & make // sure we reuse that connection. Thread.sleep(500); client.execute(target, new HttpGet("/random/2000"), response -> { EntityUtils.consume(response.getEntity()); return null; }); // Expect leased connections to be returned Assertions.assertEquals(0, connManager.getTotalStats().getLeased()); Assertions.assertEquals(1, connManager.getTotalStats().getAvailable()); } private static class WorkerThread extends Thread { private final HttpHost target; private final CloseableHttpClient httpclient; private final boolean forceClose; private final List requests; private volatile Exception exception; public WorkerThread( final CloseableHttpClient httpclient, final HttpHost target, final URI requestURI, final int repetitions, final boolean forceClose) { super(); this.httpclient = httpclient; this.target = target; this.forceClose = forceClose; this.requests = new ArrayList<>(repetitions); for (int i = 0; i < repetitions; i++) { requests.add(new HttpGet(requestURI)); } } public WorkerThread( final CloseableHttpClient httpclient, final HttpHost target, final boolean forceClose, final List requests) { super(); this.httpclient = httpclient; this.target = target; this.forceClose = forceClose; this.requests = requests; } @Override public void run() { try { for (final ClassicHttpRequest request : requests) { this.httpclient.execute(this.target, request, response -> { if (this.forceClose) { response.close(); } else { EntityUtils.consume(response.getEntity()); } return null; }); } } catch (final Exception ex) { this.exception = ex; } } public Exception getException() { return exception; } } // A very basic keep-alive header interceptor, to add Keep-Alive: timeout=1 // if there is no Connection: close header. private static class ResponseKeepAlive implements HttpResponseInterceptor { @Override public void process( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { final Header connection = response.getFirstHeader(HttpHeaders.CONNECTION); if(connection != null) { if(!connection.getValue().equalsIgnoreCase("Close")) { response.addHeader(HeaderElements.KEEP_ALIVE, "timeout=1"); } } } } } TestContentCodings.java000066400000000000000000000471351434266521000404130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; import java.util.zip.Deflater; import java.util.zip.GZIPOutputStream; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Test case for how Content Codings are processed. By default, we want to do the right thing and * require no intervention from the user of HttpClient, but we still want to let clients do their * own thing if they so wish. */ public class TestContentCodings { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public ClassicTestServer startServer() throws IOException { return testResources.startServer(null, null, null); } public CloseableHttpClient startClient(final Consumer clientCustomizer) { return testResources.startClient(clientCustomizer); } public CloseableHttpClient startClient() { return testResources.startClient(builder -> {}); } public HttpHost targetHost() { return testResources.targetHost(); } /** * Test for when we don't get an entity back; e.g. for a 204 or 304 response; nothing blows * up with the new behaviour. * * @throws Exception * if there was a problem */ @Test public void testResponseWithNoContent() throws Exception { final ClassicTestServer server = startServer(); server.registerHandler("*", new HttpRequestHandler() { /** * {@inheritDoc} */ @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_NO_CONTENT); } }); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); client.execute(target, request, response -> { Assertions.assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); Assertions.assertNull(response.getEntity()); return null; }); } /** * Test for when we are handling content from a server that has correctly interpreted RFC2616 * to return RFC1950 streams for {@code deflate} content coding. * * @throws Exception */ @Test public void testDeflateSupportForServerReturningRfc1950Stream() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createDeflateEncodingRequestHandler(entityText, false)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); client.execute(target, request, response -> { Assertions.assertEquals(entityText, EntityUtils.toString(response.getEntity()), "The entity text is correctly transported"); return null; }); } /** * Test for when we are handling content from a server that has incorrectly interpreted RFC2616 * to return RFC1951 streams for {@code deflate} content coding. * * @throws Exception */ @Test public void testDeflateSupportForServerReturningRfc1951Stream() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createDeflateEncodingRequestHandler(entityText, true)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); client.execute(target, request, response -> { Assertions.assertEquals(entityText, EntityUtils.toString(response.getEntity()), "The entity text is correctly transported"); return null; }); } /** * Test for a server returning gzipped content. * * @throws Exception */ @Test public void testGzipSupport() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createGzipEncodingRequestHandler(entityText)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); client.execute(target, request, response -> { Assertions.assertEquals(entityText, EntityUtils.toString(response.getEntity()), "The entity text is correctly transported"); return null; }); } /** * Try with a bunch of client threads, to check that it's thread-safe. * * @throws Exception * if there was a problem */ @Test public void testThreadSafetyOfContentCodings() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createGzipEncodingRequestHandler(entityText)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); /* * Create a load of workers which will access the resource. Half will use the default * gzip behaviour; half will require identity entity. */ final int clients = 100; connManager.setMaxTotal(clients); final ExecutorService executor = Executors.newFixedThreadPool(clients); final CountDownLatch startGate = new CountDownLatch(1); final CountDownLatch endGate = new CountDownLatch(clients); final List workers = new ArrayList<>(); for (int i = 0; i < clients; ++i) { workers.add(new WorkerTask(client, target, i % 2 == 0, startGate, endGate)); } for (final WorkerTask workerTask : workers) { /* Set them all in motion, but they will block until we call startGate.countDown(). */ executor.execute(workerTask); } startGate.countDown(); /* Wait for the workers to complete. */ endGate.await(); for (final WorkerTask workerTask : workers) { if (workerTask.isFailed()) { Assertions.fail("A worker failed"); } Assertions.assertEquals(entityText, workerTask.getText()); } } @Test public void testHttpEntityWriteToForGzip() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createGzipEncodingRequestHandler(entityText)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); client.execute(target, request, response -> { final ByteArrayOutputStream out = new ByteArrayOutputStream(); response.getEntity().writeTo(out); Assertions.assertEquals(entityText, out.toString("utf-8")); return null; }); } @Test public void testHttpEntityWriteToForDeflate() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createDeflateEncodingRequestHandler(entityText, true)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); client.execute(target, request, response -> { final ByteArrayOutputStream out = new ByteArrayOutputStream(); response.getEntity().writeTo(out); Assertions.assertEquals(entityText, out.toString("utf-8")); return out; }); } @Test public void gzipResponsesWorkWithBasicResponseHandler() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createGzipEncodingRequestHandler(entityText)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); final String response = client.execute(target, request, new BasicHttpClientResponseHandler()); Assertions.assertEquals(entityText, response, "The entity text is correctly transported"); } @Test public void deflateResponsesWorkWithBasicResponseHandler() throws Exception { final String entityText = "Hello, this is some plain text coming back."; final ClassicTestServer server = startServer(); server.registerHandler("*", createDeflateEncodingRequestHandler(entityText, false)); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet request = new HttpGet("/some-resource"); final String response = client.execute(target, request, new BasicHttpClientResponseHandler()); Assertions.assertEquals(entityText, response, "The entity text is correctly transported"); } /** * Creates a new {@link HttpRequestHandler} that will attempt to provide a deflate stream * Content-Coding. * * @param entityText * the non-null String entity text to be returned by the server * @param rfc1951 * if true, then the stream returned will be a raw RFC1951 deflate stream, which * some servers return as a result of misinterpreting the HTTP 1.1 RFC. If false, * then it will return an RFC2616 compliant deflate encoded zlib stream. * @return a non-null {@link HttpRequestHandler} */ private HttpRequestHandler createDeflateEncodingRequestHandler( final String entityText, final boolean rfc1951) { return new HttpRequestHandler() { /** * {@inheritDoc} */ @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setEntity(new StringEntity(entityText)); response.addHeader("Content-Type", "text/plain"); final Iterator it = MessageSupport.iterate(request, "Accept-Encoding"); while (it.hasNext()) { final HeaderElement element = it.next(); if ("deflate".equalsIgnoreCase(element.getName())) { response.addHeader("Content-Encoding", "deflate"); /* Gack. DeflaterInputStream is Java 6. */ // response.setEntity(new InputStreamEntity(new DeflaterInputStream(new // ByteArrayInputStream( // entityText.getBytes("utf-8"))), -1)); final byte[] uncompressed = entityText.getBytes(StandardCharsets.UTF_8); final Deflater compressor = new Deflater(Deflater.DEFAULT_COMPRESSION, rfc1951); compressor.setInput(uncompressed); compressor.finish(); final byte[] output = new byte[100]; final int compressedLength = compressor.deflate(output); final byte[] compressed = new byte[compressedLength]; System.arraycopy(output, 0, compressed, 0, compressedLength); response.setEntity(new InputStreamEntity( new ByteArrayInputStream(compressed), compressedLength, null)); return; } } } }; } /** * Returns an {@link HttpRequestHandler} implementation that will attempt to provide a gzip * Content-Encoding. * * @param entityText * the non-null String entity to be returned by the server * @return a non-null {@link HttpRequestHandler} */ private HttpRequestHandler createGzipEncodingRequestHandler(final String entityText) { return new HttpRequestHandler() { /** * {@inheritDoc} */ @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setEntity(new StringEntity(entityText)); response.addHeader("Content-Type", "text/plain"); response.addHeader("Content-Type", "text/plain"); final Iterator it = MessageSupport.iterate(request, "Accept-Encoding"); while (it.hasNext()) { final HeaderElement element = it.next(); if ("gzip".equalsIgnoreCase(element.getName())) { response.addHeader("Content-Encoding", "gzip"); /* * We have to do a bit more work with gzip versus deflate, since * Gzip doesn't appear to have an equivalent to DeflaterInputStream in * the JDK. * * UPDATE: DeflaterInputStream is Java 6 anyway, so we have to do a bit * of work there too! */ final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); final OutputStream out = new GZIPOutputStream(bytes); final ByteArrayInputStream uncompressed = new ByteArrayInputStream( entityText.getBytes(StandardCharsets.UTF_8)); final byte[] buf = new byte[60]; int n; while ((n = uncompressed.read(buf)) != -1) { out.write(buf, 0, n); } out.close(); final byte[] arr = bytes.toByteArray(); response.setEntity(new InputStreamEntity(new ByteArrayInputStream(arr), arr.length, null)); return; } } } }; } /** * Sub-ordinate task passed off to a different thread to be executed. * * @author jabley * */ class WorkerTask implements Runnable { private final CloseableHttpClient client; private final HttpHost target; private final HttpGet request; private final CountDownLatch startGate; private final CountDownLatch endGate; private boolean failed; private String text; WorkerTask(final CloseableHttpClient client, final HttpHost target, final boolean identity, final CountDownLatch startGate, final CountDownLatch endGate) { this.client = client; this.target = target; this.request = new HttpGet("/some-resource"); if (identity) { request.addHeader("Accept-Encoding", "identity"); } this.startGate = startGate; this.endGate = endGate; } /** * Returns the text of the HTTP entity. * * @return a String - may be null. */ public String getText() { return this.text; } /** * {@inheritDoc} */ @Override public void run() { try { startGate.await(); try { text = client.execute(target, request, response -> EntityUtils.toString(response.getEntity())); } catch (final Exception e) { failed = true; } finally { endGate.countDown(); } } catch (final InterruptedException ignore) { } } /** * Returns true if this task failed, otherwise false. * * @return a flag */ boolean isFailed() { return this.failed; } } } TestCookieVirtualHost.java000066400000000000000000000133171434266521000411030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.net.URI; import java.util.List; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * This class tests cookie matching when using Virtual Host. */ public class TestCookieVirtualHost { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); @Test public void testCookieMatchingWithVirtualHosts() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandlerVirtual("app.mydomain.fr", "*", (request, response, context) -> { final int n = Integer.parseInt(request.getFirstHeader("X-Request").getValue()); switch (n) { case 1: // Assert Host is forwarded from URI Assertions.assertEquals("app.mydomain.fr", request .getFirstHeader("Host").getValue()); response.setCode(HttpStatus.SC_OK); // Respond with Set-Cookie on virtual host domain. This // should be valid. response.addHeader(new BasicHeader("Set-Cookie", "name1=value1; domain=mydomain.fr; path=/")); break; case 2: // Assert Host is still forwarded from URI Assertions.assertEquals("app.mydomain.fr", request .getFirstHeader("Host").getValue()); // We should get our cookie back. Assertions.assertNotNull(request.getFirstHeader("Cookie"), "We must get a cookie header"); response.setCode(HttpStatus.SC_OK); break; case 3: // Assert Host is forwarded from URI Assertions.assertEquals("app.mydomain.fr", request .getFirstHeader("Host").getValue()); response.setCode(HttpStatus.SC_OK); break; default: Assertions.fail("Unexpected value: " + n); break; } }); final HttpHost target = testResources.targetHost(); final CloseableHttpClient client = testResources.startClient(b -> {}); final CookieStore cookieStore = new BasicCookieStore(); final HttpClientContext context = HttpClientContext.create(); context.setCookieStore(cookieStore); // First request : retrieve a domain cookie from remote server. final HttpGet request1 = new HttpGet(new URI("http://app.mydomain.fr")); request1.addHeader("X-Request", "1"); client.execute(target, request1, context, response -> { EntityUtils.consume(response.getEntity()); return null; }); // We should have one cookie set on domain. final List cookies = cookieStore.getCookies(); Assertions.assertNotNull(cookies); Assertions.assertEquals(1, cookies.size()); Assertions.assertEquals("name1", cookies.get(0).getName()); // Second request : send the cookie back. final HttpGet request2 = new HttpGet(new URI("http://app.mydomain.fr")); request2.addHeader("X-Request", "2"); client.execute(target, request2, context, response -> { EntityUtils.consume(response.getEntity()); return null; }); // Third request : Host header final HttpGet request3 = new HttpGet(new URI("http://app.mydomain.fr")); request3.addHeader("X-Request", "3"); client.execute(target, request3, context, response -> { EntityUtils.consume(response.getEntity()); return null; }); } } TestIdleConnectionEviction.java000066400000000000000000000123351434266521000420620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.net.URI; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.IdleConnectionEvictor; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.testing.classic.RandomHandler; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; public class TestIdleConnectionEviction { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); @Test public void testIdleConnectionEviction() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = testResources.targetHost(); final CloseableHttpClient client = testResources.startClient(b -> {}); final PoolingHttpClientConnectionManager connManager = testResources.connManager(); connManager.setDefaultMaxPerRoute(10); connManager.setMaxTotal(50); final IdleConnectionEvictor idleConnectionMonitor = new IdleConnectionEvictor(connManager, TimeValue.ofMilliseconds(50)); idleConnectionMonitor.start(); final URI requestUri = new URI("/random/1024"); final WorkerThread[] workers = new WorkerThread[5]; for (int i = 0; i < workers.length; i++) { workers[i] = new WorkerThread(client, target, requestUri, 200); } for (final WorkerThread worker : workers) { worker.start(); } for (final WorkerThread worker : workers) { worker.join(); final Exception ex = worker.getException(); if (ex != null) { throw ex; } } idleConnectionMonitor.shutdown(); } static class WorkerThread extends Thread { private final CloseableHttpClient httpclient; private final HttpHost target; private final URI requestUri; private final int count; private volatile Exception ex; public WorkerThread( final CloseableHttpClient httpclient, final HttpHost target, final URI requestUri, final int count) { super(); this.httpclient = httpclient; this.target = target; this.requestUri = requestUri; this.count = count; } @Override public void run() { try { for (int i = 0; i < this.count; i++) { final HttpGet httpget = new HttpGet(this.requestUri); this.httpclient.execute(this.target, httpget, response -> { final int status = response.getCode(); if (status != 200) { throw new ClientProtocolException("Unexpected status code: " + status); } EntityUtils.consume(response.getEntity()); try { Thread.sleep(10); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } return null; }); } } catch (final Exception ex) { this.ex = ex; } } public Exception getException() { return ex; } } } TestMalformedServerResponse.java000066400000000000000000000117261434266521000423030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.IOException; import java.net.Socket; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMalformedServerResponse { static class BrokenServerConnection extends DefaultBHttpServerConnection { public BrokenServerConnection(final Http1Config h1Config) { super(null, h1Config); } @Override public void sendResponseHeader(final ClassicHttpResponse response) throws HttpException, IOException { super.sendResponseHeader(response); if (response.getCode() == HttpStatus.SC_NO_CONTENT) { response.setEntity(new StringEntity( "garbage\ngarbage\n" + "garbage\ngarbage\n" + "garbage\ngarbage\n" + "garbage\ngarbage\n" + "garbage\ngarbage\n" + "garbage\ngarbage\n" + "garbage\ngarbage\n" + "garbage\ngarbage\n" + "garbage\ngarbage\n")); sendResponseEntity(response); } } } static class BrokenServerConnectionFactory implements HttpConnectionFactory { @Override public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException { final BrokenServerConnection conn = new BrokenServerConnection(Http1Config.DEFAULT); conn.bind(socket); return conn; } } @Test public void testNoContentResponseWithGarbage() throws Exception { try (final HttpServer server = ServerBootstrap.bootstrap() .setConnectionFactory(new BrokenServerConnectionFactory()) .register("/nostuff", (request, response, context) -> response.setCode(HttpStatus.SC_NO_CONTENT)) .register("/stuff", (request, response, context) -> { response.setCode(HttpStatus.SC_OK); response.setEntity(new StringEntity("Some important stuff")); }) .create()) { server.start(); final HttpHost target = new HttpHost("localhost", server.getLocalPort()); try (final CloseableHttpClient httpclient = HttpClientBuilder.create().build()) { final HttpGet get1 = new HttpGet("/nostuff"); httpclient.execute(target, get1, response -> { Assertions.assertEquals(HttpStatus.SC_NO_CONTENT, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpGet get2 = new HttpGet("/stuff"); httpclient.execute(target, get2, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); } } } } TestMinimalClientRequestExecution.java000066400000000000000000000124001434266521000434370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.IOException; import java.util.HashSet; import java.util.Locale; import java.util.Set; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.MinimalHttpClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Client protocol handling tests. */ public class TestMinimalClientRequestExecution { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); private static class SimpleService implements HttpRequestHandler { public SimpleService() { super(); } @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_OK); final StringEntity entity = new StringEntity("Whatever"); response.setEntity(entity); } } @Test public void testNonCompliantURIWithContext() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("*", new SimpleService()); final HttpHost target = testResources.targetHost(); final MinimalHttpClient client = testResources.startMinimalClient(); final HttpClientContext context = HttpClientContext.create(); for (int i = 0; i < 10; i++) { final HttpGet request = new HttpGet("/"); client.execute(target, request, context, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertNotNull(reqWrapper); final Header[] headers = reqWrapper.getHeaders(); final Set headerSet = new HashSet<>(); for (final Header header: headers) { headerSet.add(header.getName().toLowerCase(Locale.ROOT)); } Assertions.assertEquals(3, headerSet.size()); Assertions.assertTrue(headerSet.contains("connection")); Assertions.assertTrue(headerSet.contains("host")); Assertions.assertTrue(headerSet.contains("user-agent")); } } @Test public void testNonCompliantURIWithoutContext() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("*", new SimpleService()); final HttpHost target = testResources.targetHost(); final MinimalHttpClient client = testResources.startMinimalClient(); for (int i = 0; i < 10; i++) { final HttpGet request = new HttpGet("/"); client.execute(target, request, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); return null; }); } } } TestRedirects.java000066400000000000000000000677321434266521000374230ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.URI; import java.util.Collections; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; import org.apache.hc.client5.http.CircularRedirectException; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.RedirectException; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.RedirectLocations; import org.apache.hc.client5.testing.OldPathRedirectResolver; import org.apache.hc.client5.testing.classic.EchoHandler; import org.apache.hc.client5.testing.classic.RandomHandler; import org.apache.hc.client5.testing.classic.RedirectingDecorator; import org.apache.hc.client5.testing.redirect.Redirect; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Redirection test cases. */ public class TestRedirects { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); public ClassicTestServer startServer(final HttpProcessor httpProcessor, final Decorator handlerDecorator) throws IOException { return testResources.startServer(null, httpProcessor, handlerDecorator); } public CloseableHttpClient startClient(final Consumer clientCustomizer) { return testResources.startClient(clientCustomizer); } public CloseableHttpClient startClient() { return testResources.startClient(builder -> {}); } public HttpHost targetHost() { return testResources.targetHost(); } @Test public void testBasicRedirect300() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MULTIPLE_CHOICES))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/100"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/oldlocation/100").build(), reqWrapper.getUri()); final RedirectLocations redirects = context.getRedirectLocations(); Assertions.assertNotNull(redirects); Assertions.assertEquals(0, redirects.size()); } @Test public void testBasicRedirect300NoKeepAlive() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MULTIPLE_CHOICES, Redirect.ConnControl.CLOSE))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/100"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/oldlocation/100").build(), reqWrapper.getUri()); final RedirectLocations redirects = context.getRedirectLocations(); Assertions.assertNotNull(redirects); Assertions.assertEquals(0, redirects.size()); } @Test public void testBasicRedirect301() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/100"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(), reqWrapper.getUri()); final RedirectLocations redirects = context.getRedirectLocations(); Assertions.assertNotNull(redirects); Assertions.assertEquals(1, redirects.size()); final URI redirect = new URIBuilder().setHttpHost(target).setPath("/random/100").build(); Assertions.assertTrue(redirects.contains(redirect)); } @Test public void testBasicRedirect302() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/50"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/50").build(), reqWrapper.getUri()); } @Test public void testBasicRedirect302NoLocation() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, requestUri -> { final String path = requestUri.getPath(); if (path.startsWith("/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, null); } return null; })); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/100"); client.execute(target, httpget, context, response -> { final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getCode()); Assertions.assertEquals("/oldlocation/100", reqWrapper.getRequestUri()); EntityUtils.consume(response.getEntity()); return null; }); } @Test public void testBasicRedirect303() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_SEE_OTHER))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/123"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/123").build(), reqWrapper.getUri()); } @Test public void testBasicRedirect304() throws Exception { final ClassicTestServer server = startServer(null ,null); server.registerHandler("/oldlocation/*", (request, response, context) -> { response.setCode(HttpStatus.SC_NOT_MODIFIED); response.addHeader(HttpHeaders.LOCATION, "/random/100"); }); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/stuff"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_NOT_MODIFIED, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/oldlocation/stuff").build(), reqWrapper.getUri()); final RedirectLocations redirects = context.getRedirectLocations(); Assertions.assertNotNull(redirects); Assertions.assertEquals(0, redirects.size()); } @Test public void testBasicRedirect305() throws Exception { final ClassicTestServer server = startServer(null ,null); server.registerHandler("/oldlocation/*", (request, response, context) -> { response.setCode(HttpStatus.SC_USE_PROXY); response.addHeader(HttpHeaders.LOCATION, "/random/100"); }); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/stuff"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_USE_PROXY, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/oldlocation/stuff").build(), reqWrapper.getUri()); final RedirectLocations redirects = context.getRedirectLocations(); Assertions.assertNotNull(redirects); Assertions.assertEquals(0, redirects.size()); } @Test public void testBasicRedirect307() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_TEMPORARY_REDIRECT))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/123"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/123").build(), reqWrapper.getUri()); } @Test public void testMaxRedirectCheck() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/", HttpStatus.SC_MOVED_TEMPORARILY))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final RequestConfig config = RequestConfig.custom() .setCircularRedirectsAllowed(true) .setMaxRedirects(5) .build(); final HttpGet httpget = new HttpGet("/circular-oldlocation/123"); httpget.setConfig(config); final ClientProtocolException exception = Assertions.assertThrows(ClientProtocolException.class, () -> client.execute(target, httpget, response -> null)); Assertions.assertTrue(exception.getCause() instanceof RedirectException); } @Test public void testCircularRedirect() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/", HttpStatus.SC_MOVED_TEMPORARILY))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final RequestConfig config = RequestConfig.custom() .setCircularRedirectsAllowed(false) .build(); final HttpGet httpget = new HttpGet("/circular-oldlocation/123"); httpget.setConfig(config); final ClientProtocolException exception = Assertions.assertThrows(ClientProtocolException.class, () -> client.execute(target, httpget, response -> null)); Assertions.assertTrue(exception.getCause() instanceof CircularRedirectException); } @Test public void testPostRedirectSeeOther() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/echo", HttpStatus.SC_SEE_OTHER))); server.registerHandler("/echo/*", new EchoHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpPost httppost = new HttpPost("/oldlocation/stuff"); httppost.setEntity(new StringEntity("stuff")); client.execute(target, httppost, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/echo/stuff").build(), reqWrapper.getUri()); Assertions.assertEquals("GET", reqWrapper.getMethod()); } @Test public void testRelativeRedirect() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, requestUri -> { final String path = requestUri.getPath(); if (path.startsWith("/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/random/100"); } return null; })); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/stuff"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(), reqWrapper.getUri()); } @Test public void testRelativeRedirect2() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, requestUri -> { final String path = requestUri.getPath(); if (path.equals("/random/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "100"); } return null; })); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/random/oldlocation"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(), reqWrapper.getUri()); } @Test public void testRejectBogusRedirectLocation() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, requestUri -> { final String path = requestUri.getPath(); if (path.equals("/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "xxx://bogus"); } return null; })); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet httpget = new HttpGet("/oldlocation"); final ClientProtocolException exception = Assertions.assertThrows(ClientProtocolException.class, () -> client.execute(target, httpget, response -> null)); assertThat(exception.getCause(), CoreMatchers.instanceOf(HttpException.class)); } @Test public void testRejectInvalidRedirectLocation() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, requestUri -> { final String path = requestUri.getPath(); if (path.equals("/oldlocation")) { return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/newlocation/?p=I have spaces"); } return null; })); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpGet httpget = new HttpGet("/oldlocation"); final ClientProtocolException exception = Assertions.assertThrows(ClientProtocolException.class, () -> client.execute(target, httpget, response -> null)); assertThat(exception.getCause(), CoreMatchers.instanceOf(ProtocolException.class)); } @Test public void testRedirectWithCookie() throws Exception { final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final CookieStore cookieStore = new BasicCookieStore(); final BasicClientCookie cookie = new BasicClientCookie("name", "value"); cookie.setDomain(target.getHostName()); cookie.setPath("/"); cookieStore.addCookie(cookie); final HttpClientContext context = HttpClientContext.create(); context.setCookieStore(cookieStore); final HttpGet httpget = new HttpGet("/oldlocation/100"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(), reqWrapper.getUri()); final Header[] headers = reqWrapper.getHeaders("Cookie"); Assertions.assertEquals(1, headers.length, "There can only be one (cookie)"); } @Test public void testDefaultHeadersRedirect() throws Exception { final CloseableHttpClient client = startClient(builder -> builder .setDefaultHeaders(Collections.singletonList(new BasicHeader(HttpHeaders.USER_AGENT, "my-test-client"))) ); final ClassicTestServer server = startServer(null, requestHandler -> new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY))); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/100"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(), reqWrapper.getUri()); final Header header = reqWrapper.getFirstHeader(HttpHeaders.USER_AGENT); Assertions.assertEquals("my-test-client", header.getValue()); } @Test public void testCompressionHeaderRedirect() throws Exception { final Queue values = new ConcurrentLinkedQueue<>(); final ClassicTestServer server = startServer(null, new Decorator() { @Override public HttpServerRequestHandler decorate(final HttpServerRequestHandler requestHandler) { return new RedirectingDecorator( requestHandler, new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY)) { @Override public void handle(final ClassicHttpRequest request, final ResponseTrigger responseTrigger, final HttpContext context) throws HttpException, IOException { final Header header = request.getHeader(HttpHeaders.ACCEPT_ENCODING); if (header != null) { values.add(header.getValue()); } super.handle(request, responseTrigger, context); } }; } }); server.registerHandler("/random/*", new RandomHandler()); final HttpHost target = targetHost(); final CloseableHttpClient client = startClient(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("/oldlocation/100"); client.execute(target, httpget, context, response -> { Assertions.assertEquals(HttpStatus.SC_OK, response.getCode()); EntityUtils.consume(response.getEntity()); return null; }); final HttpRequest reqWrapper = context.getRequest(); Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(), reqWrapper.getUri()); assertThat(values.poll(), CoreMatchers.equalTo("gzip, x-gzip, deflate")); assertThat(values.poll(), CoreMatchers.equalTo("gzip, x-gzip, deflate")); assertThat(values.poll(), CoreMatchers.nullValue()); } } TestSPNegoScheme.java000066400000000000000000000215221434266521000377420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.IOException; import java.security.Principal; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.KerberosConfig; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.auth.SPNegoScheme; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSName; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; /** * Tests for {@link SPNegoScheme}. */ public class TestSPNegoScheme { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); /** * This service will continue to ask for authentication. */ private static class PleaseNegotiateService implements HttpRequestHandler { @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_UNAUTHORIZED); response.addHeader(new BasicHeader("WWW-Authenticate", StandardAuthScheme.SPNEGO + " blablabla")); response.addHeader(new BasicHeader("Connection", "Keep-Alive")); response.setEntity(new StringEntity("auth required ")); } } /** * NegotatieScheme with a custom GSSManager that does not require any Jaas or * Kerberos configuration. * */ private static class NegotiateSchemeWithMockGssManager extends SPNegoScheme { final GSSManager manager = Mockito.mock(GSSManager.class); final GSSName name = Mockito.mock(GSSName.class); final GSSContext context = Mockito.mock(GSSContext.class); NegotiateSchemeWithMockGssManager() throws Exception { super(KerberosConfig.DEFAULT, SystemDefaultDnsResolver.INSTANCE); Mockito.when(context.initSecContext( ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())) .thenReturn("12345678".getBytes()); Mockito.when(manager.createName( ArgumentMatchers.anyString(), ArgumentMatchers.any())) .thenReturn(name); Mockito.when(manager.createContext( ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.anyInt())) .thenReturn(context); } @Override protected GSSManager getManager() { return manager; } } private static class UseJaasCredentials implements Credentials { @Override public char[] getPassword() { return null; } @Override public Principal getUserPrincipal() { return null; } } private static class NegotiateSchemeFactoryWithMockGssManager implements AuthSchemeFactory { NegotiateSchemeWithMockGssManager scheme; NegotiateSchemeFactoryWithMockGssManager() throws Exception { scheme = new NegotiateSchemeWithMockGssManager(); } @Override public AuthScheme create(final HttpContext context) { return scheme; } } /** * Tests that the client will stop connecting to the server if * the server still keep asking for a valid ticket. */ @Test public void testDontTryToAuthenticateEndlessly() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("*", new PleaseNegotiateService()); final HttpHost target = testResources.targetHost(); final AuthSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager(); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(null, null, -1, null, null), new UseJaasCredentials()) .build(); final Registry authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.SPNEGO, nsf) .build(); final CloseableHttpClient client = testResources.startClient(builder -> builder .setDefaultAuthSchemeRegistry(authSchemeRegistry) .setDefaultCredentialsProvider(credentialsProvider) ); final String s = "/path"; final HttpGet httpget = new HttpGet(s); client.execute(target, httpget, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); return null; }); } /** * Javadoc specifies that {@link GSSContext#initSecContext(byte[], int, int)} can return null * if no token is generated. Client should be able to deal with this response. */ @Test public void testNoTokenGeneratedError() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("*", new PleaseNegotiateService()); final HttpHost target = testResources.targetHost(); final AuthSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager(); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(null, null, -1, null, null), new UseJaasCredentials()) .build(); final Registry authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.SPNEGO, nsf) .build(); final CloseableHttpClient client = testResources.startClient(builder -> builder .setDefaultAuthSchemeRegistry(authSchemeRegistry) .setDefaultCredentialsProvider(credentialsProvider) ); final String s = "/path"; final HttpGet httpget = new HttpGet(s); client.execute(target, httpget, response -> { EntityUtils.consume(response.getEntity()); Assertions.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode()); return null; }); } } TestSSLSocketFactory.java000066400000000000000000000366361434266521000406400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder; import org.apache.hc.client5.http.ssl.TrustAllStrategy; import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy; import org.apache.hc.client5.testing.SSLTestContexts; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.ssl.TrustStrategy; import org.apache.hc.core5.util.TimeValue; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link SSLConnectionSocketFactory}. */ public class TestSSLSocketFactory { private HttpServer server; @AfterEach public void shutDown() throws Exception { if (this.server != null) { this.server.close(CloseMode.GRACEFUL); } } static class TestX509HostnameVerifier implements HostnameVerifier { private boolean fired; @Override public boolean verify(final String host, final SSLSession session) { this.fired = true; return true; } public boolean isFired() { return this.fired; } } @Test public void testBasicSSL() throws Exception { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier(); final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( SSLTestContexts.createClientSSLContext(), hostVerifier); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket( TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) { final SSLSession sslsession = sslSocket.getSession(); Assertions.assertNotNull(sslsession); Assertions.assertTrue(hostVerifier.isFired()); } } } @Test public void testBasicDefaultHostnameVerifier() throws Exception { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); final SSLConnectionSocketFactory socketFactory = SSLConnectionSocketFactoryBuilder.create() .setSslContext(SSLTestContexts.createClientSSLContext()) .build(); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket( TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) { final SSLSession sslsession = sslSocket.getSession(); Assertions.assertNotNull(sslsession); } } } @Test public void testClientAuthSSL() throws Exception { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier(); final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( SSLTestContexts.createClientSSLContext(), hostVerifier); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket( TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) { final SSLSession sslsession = sslSocket.getSession(); Assertions.assertNotNull(sslsession); Assertions.assertTrue(hostVerifier.isFired()); } } } @Test public void testClientAuthSSLFailure() throws Exception { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .setSslSetupHandler(sslParameters -> sslParameters.setNeedClientAuth(true)) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); final TestX509HostnameVerifier hostVerifier = new TestX509HostnameVerifier(); final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( SSLTestContexts.createClientSSLContext(), hostVerifier); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); Assertions.assertThrows(IOException.class, () -> { try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket( TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) { final SSLSession sslsession = sslSocket.getSession(); Assertions.assertNotNull(sslsession); Assertions.assertTrue(hostVerifier.isFired()); sslSocket.getInputStream().read(); } }); } } @Test public void testSSLTrustVerification() throws Exception { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); // Use default SSL context final SSLContext defaultsslcontext = SSLContexts.createDefault(); final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(defaultsslcontext, NoopHostnameVerifier.INSTANCE); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); Assertions.assertThrows(SSLException.class, () -> { try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket( TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) { // empty for now } }); } } @Test public void testSSLTrustVerificationOverrideWithCustsom() throws Exception { final TrustStrategy trustStrategy = (chain, authType) -> chain.length == 1; testSSLTrustVerificationOverride(trustStrategy); } @Test public void testSSLTrustVerificationOverrideWithTrustSelfSignedStrategy() throws Exception { testSSLTrustVerificationOverride(TrustSelfSignedStrategy.INSTANCE); } @Test public void testSSLTrustVerificationOverrideWithTrustAllStrategy() throws Exception { testSSLTrustVerificationOverride(TrustAllStrategy.INSTANCE); } private void testSSLTrustVerificationOverride(final TrustStrategy trustStrategy) throws Exception, IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); // @formatter:off final SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial(null, trustStrategy) .build(); // @formatter:on final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); try (final SSLSocket sslSocket = (SSLSocket) socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)) { // empty for now } } } @Test public void testSSLDisabledByDefault() throws Exception { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {"SSLv3"})) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( SSLTestContexts.createClientSSLContext()); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); Assertions.assertThrows(IOException.class, () -> socketFactory.connectSocket( TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context)); } } @Test public void testWeakCiphersDisabledByDefault() { final String[] weakCiphersSuites = { "SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_NULL_SHA256", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5" }; for (final String cipherSuite : weakCiphersSuites) { final Exception exception = Assertions.assertThrows(Exception.class, () -> testWeakCipherDisabledByDefault(cipherSuite)); assertThat(exception, CoreMatchers.anyOf( CoreMatchers.instanceOf(IOException.class), CoreMatchers.instanceOf(IllegalArgumentException.class))); } } private void testWeakCipherDisabledByDefault(final String cipherSuite) throws Exception { // @formatter:off this.server = ServerBootstrap.bootstrap() .setSslContext(SSLTestContexts.createServerSSLContext()) .setSslSetupHandler(sslParameters -> sslParameters.setProtocols(new String[] {cipherSuite})) .create(); // @formatter:on this.server.start(); final HttpContext context = new BasicHttpContext(); final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( SSLTestContexts.createClientSSLContext()); try (final Socket socket = socketFactory.createSocket(context)) { final InetSocketAddress remoteAddress = new InetSocketAddress("localhost", this.server.getLocalPort()); final HttpHost target = new HttpHost("https", "localhost", this.server.getLocalPort()); socketFactory.connectSocket(TimeValue.ZERO_MILLISECONDS, socket, target, remoteAddress, null, context); } } } TestStatefulConnManagement.java000066400000000000000000000241441434266521000420670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import java.io.IOException; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Test cases for state-ful connections. */ public class TestStatefulConnManagement { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); public static final Timeout LONG_TIMEOUT = Timeout.ofMinutes(3); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); private static class SimpleService implements HttpRequestHandler { public SimpleService() { super(); } @Override public void handle( final ClassicHttpRequest request, final ClassicHttpResponse response, final HttpContext context) throws HttpException, IOException { response.setCode(HttpStatus.SC_OK); final StringEntity entity = new StringEntity("Whatever"); response.setEntity(entity); } } @Test public void testStatefulConnections() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("*", new SimpleService()); final HttpHost target = testResources.targetHost(); final int workerCount = 5; final int requestCount = 5; final UserTokenHandler userTokenHandler = (route, context) -> { final String id = (String) context.getAttribute("user"); return id; }; final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnTotal(workerCount) .setMaxConnPerRoute(workerCount), builder -> builder .setUserTokenHandler(userTokenHandler) ); final HttpClientContext[] contexts = new HttpClientContext[workerCount]; final HttpWorker[] workers = new HttpWorker[workerCount]; for (int i = 0; i < contexts.length; i++) { final HttpClientContext context = HttpClientContext.create(); contexts[i] = context; workers[i] = new HttpWorker( "user" + i, context, requestCount, target, client); } for (final HttpWorker worker : workers) { worker.start(); } for (final HttpWorker worker : workers) { worker.join(LONG_TIMEOUT.toMilliseconds()); } for (final HttpWorker worker : workers) { final Exception ex = worker.getException(); if (ex != null) { throw ex; } Assertions.assertEquals(requestCount, worker.getCount()); } for (final HttpContext context : contexts) { final String state0 = (String) context.getAttribute("r0"); Assertions.assertNotNull(state0); for (int r = 1; r < requestCount; r++) { Assertions.assertEquals(state0, context.getAttribute("r" + r)); } } } static class HttpWorker extends Thread { private final String uid; private final HttpClientContext context; private final int requestCount; private final HttpHost target; private final CloseableHttpClient httpclient; private volatile Exception exception; private volatile int count; public HttpWorker( final String uid, final HttpClientContext context, final int requestCount, final HttpHost target, final CloseableHttpClient httpclient) { super(); this.uid = uid; 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 { this.context.setAttribute("user", this.uid); for (int r = 0; r < this.requestCount; r++) { final HttpGet httpget = new HttpGet("/"); this.httpclient.execute(this.target, httpget, this.context, response -> { EntityUtils.consume(response.getEntity()); return null; }); this.count++; final EndpointDetails endpointDetails = this.context.getEndpointDetails(); final String connuid = Integer.toHexString(System.identityHashCode(endpointDetails)); this.context.setAttribute("r" + r, connuid); } } catch (final Exception ex) { this.exception = ex; } } } @Test public void testRouteSpecificPoolRecylcing() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("*", new SimpleService()); final HttpHost target = testResources.targetHost(); // 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 int maxConn = 2; final UserTokenHandler userTokenHandler = (route, context) -> context.getAttribute("user"); final CloseableHttpClient client = testResources.startClient( builder -> builder .setMaxConnTotal(maxConn) .setMaxConnPerRoute(maxConn), builder -> builder .setUserTokenHandler(userTokenHandler) ); // Bottom of the pool : a *keep alive* connection to Route 1. final HttpContext context1 = new BasicHttpContext(); context1.setAttribute("user", "stuff"); client.execute(target, new HttpGet("/"), context1, response -> { EntityUtils.consume(response.getEntity()); return null; }); // 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(); client.execute(new HttpHost("127.0.0.1", server.getPort()), new HttpGet("/"), context2, response -> { EntityUtils.consume(response.getEntity()); return null; }); // 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(); client.execute(target, new HttpGet("/"), context3, response -> { EntityUtils.consume(response.getEntity()); return null; }); // If the ConnPoolByRoute did not behave coherently with the RouteSpecificPool // this may fail. Ex : if the ConnPool discared the route pool because it was empty, // but still used it to build the request3 connection. } } TestWindowsNegotiateScheme.java000066400000000000000000000110771434266521000421050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync; import static java.util.concurrent.TimeUnit.MILLISECONDS; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.win.WinHttpClients; import org.apache.hc.client5.http.impl.win.WindowsNegotiateSchemeGetTokenFail; import org.apache.hc.client5.testing.sync.extension.TestClientResources; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; /** * Unit tests for Windows negotiate authentication. */ public class TestWindowsNegotiateScheme { public static final Timeout TIMEOUT = Timeout.ofMinutes(1); @RegisterExtension private TestClientResources testResources = new TestClientResources(URIScheme.HTTP, TIMEOUT); @Test // this timeout (in ms) needs to be extended if you're actively debugging the code @org.junit.jupiter.api.Timeout(value = 30000, unit = MILLISECONDS) public void testNoInfiniteLoopOnSPNOutsideDomain() throws Exception { final ClassicTestServer server = testResources.startServer(null, null, null); server.registerHandler("/", (request, response, context) -> { response.addHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.SPNEGO); response.setCode(HttpStatus.SC_UNAUTHORIZED); }); final HttpHost target = testResources.targetHost(); Assumptions.assumeTrue(WinHttpClients.isWinAuthAvailable(), "Test can only be run on Windows"); // HTTPCLIENT-1545 // If a service principal name (SPN) from outside your Windows domain tree (e.g., HTTP/example.com) is used, // InitializeSecurityContext will return SEC_E_DOWNGRADE_DETECTED (decimal: -2146892976, hex: 0x80090350). // Because WindowsNegotiateScheme wasn't setting the completed state correctly when authentication fails, // HttpClient goes into an infinite loop, constantly retrying the negotiate authentication to kingdom // come. This error message, "The system detected a possible attempt to compromise security. Please ensure that // you can contact the server that authenticated you." is associated with SEC_E_DOWNGRADE_DETECTED. final Registry authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.SPNEGO, context -> new WindowsNegotiateSchemeGetTokenFail(StandardAuthScheme.SPNEGO, "HTTP/example.com")).build(); final CloseableHttpClient client = testResources.startClient(builder -> builder .setDefaultAuthSchemeRegistry(authSchemeRegistry) ); final HttpGet httpGet = new HttpGet("/"); client.execute(target, httpGet, response -> { EntityUtils.consume(response.getEntity()); return null; }); } } extension/000077500000000000000000000000001434266521000357715ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/syncTestClientResources.java000066400000000000000000000147621434266521000426170ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/extension/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.testing.sync.extension; import java.io.IOException; import java.util.function.Consumer; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.MinimalHttpClient; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.testing.SSLTestContexts; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.io.HttpServerRequestHandler; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.testing.classic.ClassicTestServer; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestClientResources implements BeforeEachCallback, AfterEachCallback { private static final Logger LOG = LoggerFactory.getLogger(TestClientResources.class); private final URIScheme scheme; private final Timeout timeout; private ClassicTestServer server; private PoolingHttpClientConnectionManager connManager; private CloseableHttpClient client; public TestClientResources(final URIScheme scheme, final Timeout timeout) { this.scheme = scheme; this.timeout = timeout; } @Override public void beforeEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Starting up test server"); server = new ClassicTestServer( scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, SocketConfig.custom() .setSoTimeout(timeout) .build()); } @Override public void afterEach(final ExtensionContext extensionContext) throws Exception { LOG.debug("Shutting down test server"); if (client != null) { client.close(CloseMode.GRACEFUL); } if (connManager != null) { connManager.close(CloseMode.IMMEDIATE); } if (server != null) { server.shutdown(CloseMode.IMMEDIATE); } } public ClassicTestServer startServer( final Http1Config http1Config, final HttpProcessor httpProcessor, final Decorator handlerDecorator) throws IOException { Assertions.assertNotNull(server); server.start(http1Config, httpProcessor, handlerDecorator); return server; } public HttpHost targetHost() { Assertions.assertNotNull(server); return new HttpHost(scheme.id, "localhost", server.getPort()); } public CloseableHttpClient startClient( final Consumer connManagerCustomizer, final Consumer clientCustomizer) { Assertions.assertNull(connManager); Assertions.assertNull(client); final PoolingHttpClientConnectionManagerBuilder connManagerBuilder = PoolingHttpClientConnectionManagerBuilder.create(); connManagerBuilder.setDefaultSocketConfig(SocketConfig.custom() .setSoTimeout(timeout) .build()); connManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom() .setConnectTimeout(timeout) .build()); connManagerCustomizer.accept(connManagerBuilder); connManager = connManagerBuilder.build(); final HttpClientBuilder clientBuilder = HttpClientBuilder.create() .setConnectionManager(connManager); clientCustomizer.accept(clientBuilder); client = clientBuilder.build(); return client; } public MinimalHttpClient startMinimalClient() { Assertions.assertNull(connManager); Assertions.assertNull(client); final PoolingHttpClientConnectionManagerBuilder connManagerBuilder = PoolingHttpClientConnectionManagerBuilder.create(); connManagerBuilder.setDefaultSocketConfig(SocketConfig.custom() .setSoTimeout(timeout) .build()); connManagerBuilder.setDefaultConnectionConfig(ConnectionConfig.custom() .setConnectTimeout(timeout) .build()); connManager = connManagerBuilder.build(); final MinimalHttpClient minimalClient = HttpClients.createMinimal(connManager); client = minimalClient; return minimalClient; } public CloseableHttpClient startClient( final Consumer clientCustomizer) { return startClient(b -> {}, clientCustomizer); } public PoolingHttpClientConnectionManager connManager() { Assertions.assertNotNull(connManager); return connManager; } } httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/resources/000077500000000000000000000000001434266521000265275ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/resources/log4j2-debug.xml.template000066400000000000000000000030061434266521000332470ustar00rootroot00000000000000 httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/resources/log4j2.xml000066400000000000000000000022101434266521000303450ustar00rootroot00000000000000 httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/resources/test-ca.keystore000066400000000000000000000020131434266521000316520ustar00rootroot00000000000000mykeyY[X.50900  0  *H  0z1#0!U Apache Software Foundation10U HttpComponents Project10U Test CA1 0 *H  dev@hc.apache.org0  141013150120Z22880728150120Z0z1#0!U Apache Software Foundation10U HttpComponents Project10U Test CA1 0 *H  dev@hc.apache.org0"0  *H 0 xGo#3^dUp!HFogKQN<4S ^fƚ÷lBMt #cBX0ty (gY-nE+ƷoFo_0`8"8o^쩞 Պ]{%OZOK浥KhXwX dS= 8@]9~9Nolk16gDe9jQ2]rlfz⢴:0i t<P0N0Udv)/K0U#0dv)/K0 U00  *H  Q)&X:)e[]t_ gA4߼lWSbCVLU+) ,P/}h IVf7c<~W>s7KE CTkZN*$끾,?9㵵K`CXQ5Iᇿ #b:Rl #/!!lHikۻ{ܚߔ͗.fD:[zdr9eָj)`hhttpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/resources/test-ssl.txt000066400000000000000000000034651434266521000310560ustar00rootroot00000000000000# ==================================================================== # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ==================================================================== # # This software consists of voluntary contributions made by many # individuals on behalf of the Apache Software Foundation. For more # information on the Apache Software Foundation, please see # . == generate test key store with a self signed key --- keytool -genkey \ -keystore test.keystore -storepass nopassword \ -keyalg RSA -keysize 2048 \ -alias simple-http-server \ -validity 100000 \ -dname "CN=localhost, OU=Apache HttpComponents, O=Apache Software Foundation" \ -ext SAN="DNS:localhost" --- == generate test key store with a self signed key protected with a key password --- keytool -genkey \ -keystore test-keypasswd.keystore -storepass nopassword \ -keyalg RSA -keysize 2048 -keypass password \ -alias simple-http-server \ -validity 100000 \ -dname "CN=localhost, OU=Apache HttpComponents, O=Apache Software Foundation" \ -ext SAN="DNS:localhost" --- httpcomponents-client-rel-v5.2.1/httpclient5-testing/src/test/resources/test.keystore000066400000000000000000000043031434266521000312750ustar00rootroot00000000000000simple-http-serverfL00 +*.UŻb 6>XpFef7K݀9gE7,\?5ؗwnsu,r /P5MY%ppϢ%^U Hvˣv۱};TDA,Q2fmd!D@wF:z>+bc=$gŔ]/Dw!3̐lI`۱:%BeՎ{x8wNT=x2&R)ul[v AA߲4W 5. #ގ@sp!p}E]z3,;ldTVBzZEp:>$s/6-RBrۍ$OBi622]KDbWcVsFpݫn}T~,~16tM'F^cj<'8i,ڔ}8!TaÍGCG © ڎM؏<ϟzP\HW[´H#l֗7Bo`CN4U~9;#l S肽$g7Y A:*B{=Wgh}mӳZР6j P^&:mpF#g}(ǷnĠAD"1f=hh9CʔgSƞ)l_G6~ڃ4t^gFPUr{}dfD(;TZ2= ``{lMG홰J4۩bNI&"1}܉6|IT]pi2&fx#h{M Y8Њ y SGTf<kgk.YpJ!-eЯן~M?wZFQ0!ꀚEpDr#EސɶN dj: ߍO![!c.mħ(>ΩM(L.רUYp"pA{ n 7OA'QZգHS@-9w9P0 !#y#07~LላfUDTX.509m0i0QAv0  *H  0Y1#0!U Apache Software Foundation10U Apache HttpComponents10U localhost0  181019104659Z22920803104659Z0Y1#0!U Apache Software Foundation10U Apache HttpComponents10U localhost0"0  *H 0 * 䌌جG'1!1cƻt!FСS`Rp&OuD BўU=hOzgƁ_IF:,[MLݽ Feo!jbs}?sCw"cB-tJqn K"i~h:u۶yYLz-hi1Cf-̯[V㌢c,OTHBCoׯ)d\#t έ >6ה:š0H XO+I7050U 0 localhost0UJK`+XҬ$N0  *H  %GyӐs5&|AHk-*ugB>t u'e!.\9SϘAX%7S|9bТhp:v_f9`[r Pb~tyٜ쐛}h=4!UbSbc˶UP/qkR!/PdO]M{#kXۢT y~PL/ 4.0.0 org.apache.httpcomponents.client5 httpclient5-parent 5.2.1 httpclient5-win Apache HttpClient Windows features Apache HttpClient Windows specific functionality jar org.apache.httpcomponents.client5.httpclient5.win org.apache.httpcomponents.client5 httpclient5 org.apache.httpcomponents.client5 httpclient5 test tests org.slf4j slf4j-api org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test net.java.dev.jna jna compile net.java.dev.jna jna-platform org.junit.jupiter junit-jupiter-migrationsupport test org.hamcrest hamcrest test maven-project-info-reports-plugin false index dependencies dependency-info summary httpcomponents-client-rel-v5.2.1/httpclient5-win/src/000077500000000000000000000000001434266521000226565ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/000077500000000000000000000000001434266521000244745ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/000077500000000000000000000000001434266521000252635ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/000077500000000000000000000000001434266521000265045ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/hc/000077500000000000000000000000001434266521000270765ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/hc/client5/000077500000000000000000000000001434266521000304415ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/hc/client5/http/000077500000000000000000000000001434266521000314205ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/hc/client5/http/examples/000077500000000000000000000000001434266521000332365ustar00rootroot00000000000000client/000077500000000000000000000000001434266521000344355ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/hc/client5/http/exampleswin/000077500000000000000000000000001434266521000352325ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/hc/client5/http/examples/clientClientWinAuth.java000066400000000000000000000054211434266521000406150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/examples/org/apache/hc/client5/http/examples/client/win/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples.client.win; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.win.WinHttpClients; import org.apache.hc.core5.http.io.entity.EntityUtils; /** * This example demonstrates how to create HttpClient pre-configured * with support for integrated Windows authentication. */ public class ClientWinAuth { public final static void main(String[] args) throws Exception { if (!WinHttpClients.isWinAuthAvailable()) { System.out.println("Integrated Win auth is not supported!!!"); } CloseableHttpClient httpclient = WinHttpClients.createDefault(); // There is no need to provide user credentials // HttpClient will attempt to access current user security context through // Windows platform specific methods via JNI. try { HttpGet httpget = new HttpGet("http://winhost/"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); CloseableHttpResponse response = httpclient.execute(httpget); try { System.out.println("----------------------------------------"); System.out.println(response.getCode() + " " + response.getReasonPhrase()); EntityUtils.consume(response.getEntity()); } finally { response.close(); } } finally { httpclient.close(); } } } httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/000077500000000000000000000000001434266521000236025ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/000077500000000000000000000000001434266521000245235ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/000077500000000000000000000000001434266521000253125ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/000077500000000000000000000000001434266521000265335ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/000077500000000000000000000000001434266521000271255ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/000077500000000000000000000000001434266521000304705ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000314475ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/impl/000077500000000000000000000000001434266521000324105ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/impl/win/000077500000000000000000000000001434266521000332055ustar00rootroot00000000000000WinHttpClients.java000066400000000000000000000071351434266521000367160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/impl/win/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.win; import java.util.Locale; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory; import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; /** * Factory methods for {@link org.apache.hc.client5.http.impl.classic.CloseableHttpClient} instances configured to use integrated * Windows authentication by default. * * @since 4.4 */ public class WinHttpClients { private WinHttpClients() { super(); } public static boolean isWinAuthAvailable() { String os = System.getProperty("os.name"); os = os != null ? os.toLowerCase(Locale.ROOT) : null; return os != null && os.contains("windows"); } private static HttpClientBuilder createBuilder() { if (isWinAuthAvailable()) { final Registry authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE) .register(StandardAuthScheme.NTLM, WindowsNTLMSchemeFactory.DEFAULT) .register(StandardAuthScheme.SPNEGO, WindowsNegotiateSchemeFactory.DEFAULT) .build(); return HttpClientBuilder.create() .setDefaultAuthSchemeRegistry(authSchemeRegistry); } return HttpClientBuilder.create(); } /** * Creates builder object for construction of custom * {@link CloseableHttpClient} instances. */ public static HttpClientBuilder custom() { return createBuilder(); } /** * Creates {@link CloseableHttpClient} instance with default * configuration. */ public static CloseableHttpClient createDefault() { return createBuilder().build(); } /** * Creates {@link CloseableHttpClient} instance with default * configuration based on system properties. */ public static CloseableHttpClient createSystem() { return createBuilder().useSystemProperties().build(); } } WindowsNTLMSchemeFactory.java000066400000000000000000000046071434266521000406020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/impl/win/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.win; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link AuthSchemeFactory} implementation that creates and initializes * {@link WindowsNegotiateScheme} using JNA to implement NTLM * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) @Experimental public class WindowsNTLMSchemeFactory implements AuthSchemeFactory { /** * Singleton instance with a null name. */ public static final WindowsNTLMSchemeFactory DEFAULT = new WindowsNTLMSchemeFactory(null); private final String servicePrincipalName; public WindowsNTLMSchemeFactory(final String servicePrincipalName) { super(); this.servicePrincipalName = servicePrincipalName; } @Override public AuthScheme create(final HttpContext context) { return new WindowsNegotiateScheme(StandardAuthScheme.NTLM, servicePrincipalName); } } WindowsNegotiateScheme.java000066400000000000000000000256541434266521000404240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/impl/win/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.win; import java.security.Principal; import org.apache.hc.client5.http.utils.Base64; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.BasicUserPrincipal; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.jna.platform.win32.Secur32; import com.sun.jna.platform.win32.Secur32Util; import com.sun.jna.platform.win32.Sspi; import com.sun.jna.platform.win32.Sspi.CredHandle; import com.sun.jna.platform.win32.Sspi.CtxtHandle; import com.sun.jna.platform.win32.Sspi.SecBufferDesc; import com.sun.jna.platform.win32.Sspi.TimeStamp; import com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc; import com.sun.jna.platform.win32.Win32Exception; import com.sun.jna.platform.win32.WinError; import com.sun.jna.ptr.IntByReference; /** * Auth scheme that makes use of JNA to implement Negotiate and NTLM on Windows Platforms. *

* This will delegate negotiation to the windows machine. *

* * @since 4.4 */ @Experimental public class WindowsNegotiateScheme implements AuthScheme { private static final Logger LOG = LoggerFactory.getLogger(WindowsNegotiateScheme.class); // NTLM or Negotiate private final String schemeName; private final String servicePrincipalName; private ChallengeType challengeType; private String challenge; private CredHandle clientCred; private CtxtHandle sspiContext; private boolean continueNeeded; WindowsNegotiateScheme(final String schemeName, final String servicePrincipalName) { super(); this.schemeName = (schemeName == null) ? StandardAuthScheme.SPNEGO : schemeName; this.continueNeeded = true; this.servicePrincipalName = servicePrincipalName; if (LOG.isDebugEnabled()) { LOG.debug("Created WindowsNegotiateScheme using {}", this.schemeName); } } public void dispose() { if (clientCred != null && !clientCred.isNull()) { final int rc = Secur32.INSTANCE.FreeCredentialsHandle(clientCred); if (WinError.SEC_E_OK != rc) { throw new Win32Exception(rc); } } if (sspiContext != null && !sspiContext.isNull()) { final int rc = Secur32.INSTANCE.DeleteSecurityContext(sspiContext); if (WinError.SEC_E_OK != rc) { throw new Win32Exception(rc); } } continueNeeded = true; // waiting clientCred = null; sspiContext = null; } @Override public String getName() { return schemeName; } @Override public boolean isConnectionBased() { return true; } @Override public String getRealm() { return null; } @Override public void processChallenge( final AuthChallenge authChallenge, final HttpContext context) throws MalformedChallengeException { Args.notNull(authChallenge, "AuthChallenge"); challengeType = authChallenge.getChallengeType(); challenge = authChallenge.getValue(); if (TextUtils.isBlank(challenge)) { if (clientCred != null) { dispose(); // run cleanup first before throwing an exception otherwise can leak OS resources if (continueNeeded) { throw new IllegalStateException("Unexpected token"); } } } } @Override public boolean isResponseReady( final HttpHost host, final CredentialsProvider credentialsProvider, final HttpContext context) throws AuthenticationException { return true; } /** * Get the SAM-compatible username of the currently logged-on user. * * @return String. */ public static String getCurrentUsername() { return Secur32Util.getUserNameEx(Secur32.EXTENDED_NAME_FORMAT.NameSamCompatible); } @Override public Principal getPrincipal() { return new BasicUserPrincipal(getCurrentUsername()); } @Override public String generateAuthResponse( final HttpHost host, final HttpRequest request, final HttpContext context) throws AuthenticationException { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String response; if (clientCred == null) { // client credentials handle try { final String username = getCurrentUsername(); final TimeStamp lifetime = new TimeStamp(); clientCred = new CredHandle(); final int rc = Secur32.INSTANCE.AcquireCredentialsHandle(username, schemeName, Sspi.SECPKG_CRED_OUTBOUND, null, null, null, null, clientCred, lifetime); if (WinError.SEC_E_OK != rc) { throw new Win32Exception(rc); } final String targetName = getServicePrincipalName(request, clientContext); response = getToken(null, null, targetName); } catch (final RuntimeException ex) { failAuthCleanup(); if (ex instanceof Win32Exception) { throw new AuthenticationException("Authentication Failed", ex); } throw ex; } } else if (challenge == null || challenge.isEmpty()) { failAuthCleanup(); throw new AuthenticationException("Authentication Failed"); } else { try { final byte[] continueTokenBytes = Base64.decodeBase64(challenge); final SecBufferDesc continueTokenBuffer = new ManagedSecBufferDesc( Sspi.SECBUFFER_TOKEN, continueTokenBytes); final String targetName = getServicePrincipalName(request, clientContext); response = getToken(this.sspiContext, continueTokenBuffer, targetName); } catch (final RuntimeException ex) { failAuthCleanup(); if (ex instanceof Win32Exception) { throw new AuthenticationException("Authentication Failed", ex); } throw ex; } } return schemeName + " " + response; } private void failAuthCleanup() { dispose(); this.continueNeeded = false; } // Per RFC4559, the Service Principal Name should HTTP/. However, // can just be the host or the fully qualified name (e.g., see "Kerberos SPN generation" // at http://www.chromium.org/developers/design-documents/http-authentication). Here, // I've chosen to use the host that has been provided in HttpHost so that I don't incur // any additional DNS lookup cost. private String getServicePrincipalName(final HttpRequest request, final HttpClientContext clientContext) { String spn = null; if (this.servicePrincipalName != null) { spn = this.servicePrincipalName; } else if (challengeType == ChallengeType.PROXY) { final RouteInfo route = clientContext.getHttpRoute(); if (route != null) { spn = "HTTP/" + route.getProxyHost().getHostName(); } } else { final URIAuthority authority = request.getAuthority(); if (authority != null) { spn = "HTTP/" + authority.getHostName(); } else { final RouteInfo route = clientContext.getHttpRoute(); if (route != null) { spn = "HTTP/" + route.getTargetHost().getHostName(); } } } if (LOG.isDebugEnabled()) { LOG.debug("Using SPN: {}", spn); } return spn; } // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa375506(v=vs.85).aspx String getToken( final CtxtHandle continueCtx, final SecBufferDesc continueToken, final String targetName) { final IntByReference attr = new IntByReference(); final ManagedSecBufferDesc token = new ManagedSecBufferDesc( Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); sspiContext = new CtxtHandle(); final int rc = Secur32.INSTANCE.InitializeSecurityContext(clientCred, continueCtx, targetName, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH, 0, Sspi.SECURITY_NATIVE_DREP, continueToken, 0, sspiContext, token, attr, null); switch (rc) { case WinError.SEC_I_CONTINUE_NEEDED: continueNeeded = true; break; case WinError.SEC_E_OK: dispose(); // Don't keep the context continueNeeded = false; break; default: dispose(); throw new Win32Exception(rc); } return Base64.encodeBase64String(token.getBuffer(0).getBytes()); } @Override public boolean isChallengeComplete() { return !continueNeeded; } } WindowsNegotiateSchemeFactory.java000066400000000000000000000046441434266521000417500ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/impl/win/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.win; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link AuthSchemeFactory} implementation that creates and initializes * {@link WindowsNegotiateScheme} using JNA to Negotiate credentials * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) @Experimental public class WindowsNegotiateSchemeFactory implements AuthSchemeFactory { /** * Singleton instance with a null name. */ public static final WindowsNegotiateSchemeFactory DEFAULT = new WindowsNegotiateSchemeFactory(null); private final String servicePrincipalName; public WindowsNegotiateSchemeFactory(final String servicePrincipalName) { super(); this.servicePrincipalName = servicePrincipalName; } @Override public AuthScheme create(final HttpContext context) { return new WindowsNegotiateScheme(StandardAuthScheme.SPNEGO, servicePrincipalName); } } package-info.java000066400000000000000000000026521434266521000363220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/main/java/org/apache/hc/client5/http/impl/win/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Auth scheme that makes use of JNA to implement Negotiate and NTLM on Windows Platforms. *

* Please note this class is considered experimental and may be discontinued or removed * in the future. *

*/ package org.apache.hc.client5.http.impl.win; httpcomponents-client-rel-v5.2.1/httpclient5-win/src/test/000077500000000000000000000000001434266521000236355ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/test/resources/000077500000000000000000000000001434266521000256475ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5-win/src/test/resources/log4j2.xml000066400000000000000000000022071434266521000274730ustar00rootroot00000000000000 httpcomponents-client-rel-v5.2.1/httpclient5/000077500000000000000000000000001434266521000212745ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/pom.xml000066400000000000000000000135121434266521000226130ustar00rootroot00000000000000 4.0.0 org.apache.httpcomponents.client5 httpclient5-parent 5.2.1 httpclient5 Apache HttpClient Apache HttpComponents Client jar org.apache.httpcomponents.client5.httpclient5 org.apache.httpcomponents.core5 httpcore5 org.apache.httpcomponents.core5 httpcore5-h2 org.slf4j slf4j-api org.conscrypt conscrypt-openjdk-uber true org.apache.httpcomponents.core5 httpcore5-reactive test io.reactivex.rxjava2 rxjava test org.apache.logging.log4j log4j-slf4j-impl test org.apache.logging.log4j log4j-core test org.brotli dec true org.junit.jupiter junit-jupiter test org.hamcrest hamcrest test org.mockito mockito-core test src/main/resources true **/*.properties com.googlecode.maven-download-plugin download-maven-plugin 1.6.8 download-public-suffix-list generate-resources wget https://publicsuffix.org/list/effective_tld_names.dat ${project.build.outputDirectory}/mozilla public-suffix-list.txt org.apache.maven.plugins maven-jar-plugin test-jar maven-project-info-reports-plugin false index dependencies dependency-info summary apache-release com.googlecode.maven-download-plugin download-maven-plugin true true httpcomponents-client-rel-v5.2.1/httpclient5/src/000077500000000000000000000000001434266521000220635ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/000077500000000000000000000000001434266521000230075ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/000077500000000000000000000000001434266521000237305ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/000077500000000000000000000000001434266521000245175ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/000077500000000000000000000000001434266521000257405ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/000077500000000000000000000000001434266521000263325ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/000077500000000000000000000000001434266521000276755ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000306545ustar00rootroot00000000000000AuthenticationStrategy.java000066400000000000000000000050761434266521000361520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.util.List; import java.util.Map; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * Strategy to select auth schemes in order of preference based on auth challenges * presented by the opposite endpoint (target server or a proxy). *

* Implementations of this interface must be thread-safe. Access to shared data must be * synchronized as methods of this interface may be executed from multiple threads. * * @since 4.2 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface AuthenticationStrategy { /** * Returns an list of {@link AuthScheme}s to handle the given {@link AuthChallenge}s * in their order of preference. * * @param challengeType challenge type. * @param challenges map of challenges keyed by lowercase auth scheme names. * @param context HTTP context. * @return authentication auth schemes that can be used for authentication. Can be empty. * * @since 5.0 */ List select( ChallengeType challengeType, Map challenges, HttpContext context); } CircularRedirectException.java000066400000000000000000000043441434266521000365520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; /** * Signals a circular redirect * * @since 4.0 */ public class CircularRedirectException extends RedirectException { private static final long serialVersionUID = 6830063487001091803L; /** * Creates a new CircularRedirectException with a {@code null} detail message. */ public CircularRedirectException() { super(); } /** * Creates a new CircularRedirectException with the specified detail message. * * @param message The exception detail message */ public CircularRedirectException(final String message) { super(message); } /** * Creates a new CircularRedirectException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public CircularRedirectException(final String message, final Throwable cause) { super(message, cause); } } ClientProtocolException.java000066400000000000000000000034421434266521000362620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.io.IOException; /** * Signals an error in the HTTP protocol. * * @since 4.0 */ public class ClientProtocolException extends IOException { private static final long serialVersionUID = -5596590843227115865L; public ClientProtocolException() { super(); } public ClientProtocolException(final String s) { super(s); } public ClientProtocolException(final Throwable cause) { initCause(cause); } public ClientProtocolException(final String message, final Throwable cause) { super(message); initCause(cause); } } ConnectExceptionSupport.java000066400000000000000000000073241434266521000363130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.io.IOException; import java.net.ConnectException; import java.net.InetAddress; import java.net.SocketTimeoutException; import java.util.Arrays; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.net.NamedEndpoint; /** * Connect exception support methods. * * @since 5.0 */ @Internal public final class ConnectExceptionSupport { public static ConnectTimeoutException createConnectTimeoutException( final IOException cause, final NamedEndpoint namedEndpoint, final InetAddress... remoteAddresses) { final String message = "Connect to " + (namedEndpoint != null ? namedEndpoint : "remote endpoint") + (remoteAddresses != null && remoteAddresses.length > 0 ? " " + Arrays.asList(remoteAddresses) : "") + ((cause != null && cause.getMessage() != null) ? " failed: " + cause.getMessage() : " timed out"); return new ConnectTimeoutException(message, namedEndpoint); } public static HttpHostConnectException createHttpHostConnectException( final IOException cause, final NamedEndpoint namedEndpoint, final InetAddress... remoteAddresses) { final String message = "Connect to " + (namedEndpoint != null ? namedEndpoint : "remote endpoint") + (remoteAddresses != null && remoteAddresses.length > 0 ? " " + Arrays.asList(remoteAddresses) : "") + ((cause != null && cause.getMessage() != null) ? " failed: " + cause.getMessage() : " refused"); return new HttpHostConnectException(message, namedEndpoint); } public static IOException enhance( final IOException cause, final NamedEndpoint namedEndpoint, final InetAddress... remoteAddresses) { if (cause instanceof SocketTimeoutException) { final IOException ex = createConnectTimeoutException(cause, namedEndpoint, remoteAddresses); ex.setStackTrace(cause.getStackTrace()); return ex; } else if (cause instanceof ConnectException) { if ("Connection timed out".equals(cause.getMessage())) { final IOException ex = createConnectTimeoutException(cause, namedEndpoint, remoteAddresses); ex.initCause(cause); return ex; } final IOException ex = createHttpHostConnectException(cause, namedEndpoint, remoteAddresses); ex.setStackTrace(cause.getStackTrace()); return ex; } else { return cause; } } } ConnectTimeoutException.java000066400000000000000000000041361434266521000362630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.net.SocketTimeoutException; import org.apache.hc.core5.net.NamedEndpoint; /** * A timeout while connecting to an HTTP server or waiting for an available connection from a connection manager. * * @since 4.0 */ public class ConnectTimeoutException extends SocketTimeoutException { private static final long serialVersionUID = -4816682903149535989L; private final NamedEndpoint namedEndpoint; /** * Creates a ConnectTimeoutException with the specified detail message. */ public ConnectTimeoutException(final String message) { super(message); this.namedEndpoint = null; } public ConnectTimeoutException(final String message, final NamedEndpoint namedEndpoint) { super(message); this.namedEndpoint = namedEndpoint; } /** * @since 5.0 */ public NamedEndpoint getHost() { return this.namedEndpoint; } } ConnectionKeepAliveStrategy.java000066400000000000000000000056241434266521000370570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; /** * Interface for deciding how long a connection can remain * idle before being reused. *

* 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 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ConnectionKeepAliveStrategy { /** * Returns the duration of time which this connection can be safely kept * idle. If the connection is left idle for longer than this period of time, * it MUST not reused. A value of 0 or less may be returned to indicate that * there is no suitable suggestion. * * When coupled with a {@link org.apache.hc.core5.http.ConnectionReuseStrategy}, if * {@link org.apache.hc.core5.http.ConnectionReuseStrategy#keepAlive( * org.apache.hc.core5.http.HttpRequest, HttpResponse, HttpContext)} returns true, * this allows you to control how long the reuse will last. If keepAlive returns * false, this should have no meaningful impact * * @param response * The last response received over the connection. * @param context * the context in which the connection is being used. * * @return the duration in ms for which it is safe to keep the connection * idle, or <=0 if no suggested duration. */ TimeValue getKeepAliveDuration(HttpResponse response, HttpContext context); } ContextBuilder.java000066400000000000000000000121571434266521000344010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.util.Args; /** * {@link HttpClientContext} builder. * * @since 5.2 */ public class ContextBuilder { private final SchemePortResolver schemePortResolver; private Lookup cookieSpecRegistry; private Lookup authSchemeRegistry; private CookieStore cookieStore; private CredentialsProvider credentialsProvider; private AuthCache authCache; private Map authSchemeMap; ContextBuilder(final SchemePortResolver schemePortResolver) { this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; } public static ContextBuilder create(final SchemePortResolver schemePortResolver) { return new ContextBuilder(schemePortResolver); } public static ContextBuilder create() { return new ContextBuilder(DefaultSchemePortResolver.INSTANCE); } public ContextBuilder useCookieSpecRegistry(final Lookup cookieSpecRegistry) { this.cookieSpecRegistry = cookieSpecRegistry; return this; } public ContextBuilder useAuthSchemeRegistry(final Lookup authSchemeRegistry) { this.authSchemeRegistry = authSchemeRegistry; return this; } public ContextBuilder useCookieStore(final CookieStore cookieStore) { this.cookieStore = cookieStore; return this; } public ContextBuilder useCredentialsProvider(final CredentialsProvider credentialsProvider) { this.credentialsProvider = credentialsProvider; return this; } public ContextBuilder useAuthCache(final AuthCache authCache) { this.authCache = authCache; return this; } public ContextBuilder preemptiveAuth(final HttpHost host, final AuthScheme authScheme) { Args.notNull(host, "HTTP host"); if (authSchemeMap == null) { authSchemeMap = new HashMap<>(); } authSchemeMap.put(RoutingSupport.normalize(host, schemePortResolver), authScheme); return this; } public ContextBuilder preemptiveBasicAuth(final HttpHost host, final UsernamePasswordCredentials credentials) { Args.notNull(host, "HTTP host"); final BasicScheme authScheme = new BasicScheme(StandardCharsets.UTF_8); authScheme.initPreemptive(credentials); preemptiveAuth(host, authScheme); return this; } public HttpClientContext build() { final HttpClientContext context = new HttpClientContext(new BasicHttpContext()); context.setCookieSpecRegistry(cookieSpecRegistry); context.setAuthSchemeRegistry(authSchemeRegistry); context.setCookieStore(cookieStore); context.setCredentialsProvider(credentialsProvider); context.setAuthCache(authCache); if (authSchemeMap != null) { for (final Map.Entry entry : authSchemeMap.entrySet()) { context.resetAuthExchange(entry.getKey(), entry.getValue()); } } return context; } } DnsResolver.java000066400000000000000000000044261434266521000337140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.net.InetAddress; import java.net.UnknownHostException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Users may implement this interface to override the normal DNS lookup offered * by the OS. * * @since 4.2 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface DnsResolver { /** * Returns the IP address for the specified host name, or null if the given * host is not recognized or the associated IP address cannot be used to * build an InetAddress instance. * * @see InetAddress * * @param host * The host name to be resolved by this resolver. * @return The IP address associated to the given host name, or null if the * host name is not known by the implementation class. */ InetAddress[] resolve(String host) throws UnknownHostException; /** * Gets the fully qualified domain name for given host name. * @since 5.0 */ String resolveCanonicalHostname(String host) throws UnknownHostException; } HttpHostConnectException.java000066400000000000000000000041071434266521000364100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.net.ConnectException; import org.apache.hc.core5.net.NamedEndpoint; /** * A {@link ConnectException} that specifies the {@link NamedEndpoint} that was being connected to. * * @since 4.0 */ public class HttpHostConnectException extends ConnectException { private static final long serialVersionUID = -3194482710275220224L; private final NamedEndpoint namedEndpoint; /** * Creates a HttpHostConnectException with the specified detail message. */ public HttpHostConnectException(final String message) { super(message); this.namedEndpoint = null; } public HttpHostConnectException(final String message, final NamedEndpoint namedEndpoint) { super(message); this.namedEndpoint = namedEndpoint; } /** * @since 5.0 */ public NamedEndpoint getHost() { return this.namedEndpoint; } } HttpRequestRetryStrategy.java000066400000000000000000000100441434266521000365000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; /** * Strategy interface that allows API users to plug in their own logic to * control whether or not a retry should automatically be done, how many times * it should be done and so on. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpRequestRetryStrategy { /** * Determines if a method should be retried after an I/O exception * occurred during execution. * * @param request the request failed due to an I/O exception * @param exception the exception that occurred * @param execCount the number of times this method has been * unsuccessfully executed * @param context the context for the request execution * * @return {@code true} if the request should be retried, {@code false} * otherwise */ boolean retryRequest(HttpRequest request, IOException exception, int execCount, HttpContext context); /** * Determines if a method should be retried given the response from * the target server. * * @param response the response from the target server * @param execCount the number of times this method has been * unsuccessfully executed * @param context the context for the request execution * * @return {@code true} if the request should be retried, {@code false} * otherwise */ boolean retryRequest(HttpResponse response, int execCount, HttpContext context); /** * Determines the retry interval between subsequent retries. * * @param request the request failed due to an I/O exception * @param exception the exception that occurred * @param execCount the number of times this method has been * unsuccessfully executed * @param context the context for the request execution * * @return the retry interval between subsequent retries */ default TimeValue getRetryInterval(HttpRequest request, IOException exception, int execCount, HttpContext context) { return TimeValue.ZERO_MILLISECONDS; } /** * Determines the retry interval between subsequent retries. * * @param response the response from the target server * @param execCount the number of times this method has been * unsuccessfully executed * @param context the context for the request execution * * @return the retry interval between subsequent retries */ TimeValue getRetryInterval(HttpResponse response, int execCount, HttpContext context); } HttpResponseException.java000066400000000000000000000037471434266521000357700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import org.apache.hc.core5.util.TextUtils; /** * Signals a non 2xx HTTP response. * * @since 4.0 */ public class HttpResponseException extends ClientProtocolException { private static final long serialVersionUID = -7186627969477257933L; private final int statusCode; private final String reasonPhrase; public HttpResponseException(final int statusCode, final String reasonPhrase) { super(String.format("status code: %d" + (TextUtils.isBlank(reasonPhrase) ? "" : ", reason phrase: %s"), statusCode, reasonPhrase)); this.statusCode = statusCode; this.reasonPhrase = reasonPhrase; } public int getStatusCode() { return this.statusCode; } public String getReasonPhrase() { return this.reasonPhrase; } } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRoute.java000066400000000000000000000265131434266521000334640ustar00rootroot00000000000000/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; /** * Connection route definition for HTTP requests. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class HttpRoute implements RouteInfo, Cloneable { /** The target host to connect to. */ private final HttpHost targetHost; /** * The local address to connect from. * {@code null} indicates that the default should be used. */ private final InetAddress localAddress; /** The proxy servers, if any. Never null. */ private final List proxyChain; /** Whether the the route is tunnelled through the proxy. */ private final TunnelType tunnelled; /** Whether the route is layered. */ private final LayerType layered; /** Whether the route is (supposed to be) secure. */ private final boolean secure; private HttpRoute(final HttpHost targetHost, final InetAddress local, final List proxies, final boolean secure, final TunnelType tunnelled, final LayerType layered) { Args.notNull(targetHost, "Target host"); Args.notNegative(targetHost.getPort(), "Target port"); this.targetHost = targetHost; this.localAddress = local; if (proxies != null && !proxies.isEmpty()) { this.proxyChain = new ArrayList<>(proxies); } else { this.proxyChain = null; } if (tunnelled == TunnelType.TUNNELLED) { Args.check(this.proxyChain != null, "Proxy required if tunnelled"); } this.secure = secure; this.tunnelled = tunnelled != null ? tunnelled : TunnelType.PLAIN; this.layered = layered != null ? layered : LayerType.PLAIN; } /** * Creates a new route with all attributes specified explicitly. * * @param target the host to which to route * @param local the local address to route from, or * {@code null} for the default * @param proxies the proxy chain to use, or * {@code null} for a direct route * @param secure {@code true} if the route is (to be) secure, * {@code false} otherwise * @param tunnelled the tunnel type of this route * @param layered the layering type of this route */ public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost[] proxies, final boolean secure, final TunnelType tunnelled, final LayerType layered) { this(target, local, proxies != null ? Arrays.asList(proxies) : null, secure, tunnelled, layered); } /** * Creates a new route with at most one proxy. * * @param target the host to which to route * @param local the local address to route from, or * {@code null} for the default * @param proxy the proxy to use, or * {@code null} for a direct route * @param secure {@code true} if the route is (to be) secure, * {@code false} otherwise * @param tunnelled {@code true} if the route is (to be) tunnelled * via the proxy, * {@code false} otherwise * @param layered {@code true} if the route includes a * layered protocol, * {@code false} otherwise */ public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost proxy, final boolean secure, final TunnelType tunnelled, final LayerType layered) { this(target, local, proxy != null ? Collections.singletonList(proxy) : null, secure, tunnelled, layered); } /** * Creates a new direct route. * That is a route without a proxy. * * @param target the host to which to route * @param local the local address to route from, or * {@code null} for the default * @param secure {@code true} if the route is (to be) secure, * {@code false} otherwise */ public HttpRoute(final HttpHost target, final InetAddress local, final boolean secure) { this(target, local, Collections.emptyList(), secure, TunnelType.PLAIN, LayerType.PLAIN); } /** * Creates a new direct insecure route. * * @param target the host to which to route */ public HttpRoute(final HttpHost target) { this(target, null, Collections.emptyList(), false, TunnelType.PLAIN, LayerType.PLAIN); } /** * Creates a new route through a proxy. * When using this constructor, the {@code proxy} MUST be given. * For convenience, it is assumed that a secure connection will be * layered over a tunnel through the proxy. * * @param target the host to which to route * @param local the local address to route from, or * {@code null} for the default * @param proxy the proxy to use * @param secure {@code true} if the route is (to be) secure, * {@code false} otherwise */ public HttpRoute(final HttpHost target, final InetAddress local, final HttpHost proxy, final boolean secure) { this(target, local, Collections.singletonList(Args.notNull(proxy, "Proxy host")), secure, secure ? TunnelType.TUNNELLED : TunnelType.PLAIN, secure ? LayerType.LAYERED : LayerType.PLAIN); } /** * Creates a new plain route through a proxy. * * @param target the host to which to route * @param proxy the proxy to use * * @since 4.3 */ public HttpRoute(final HttpHost target, final HttpHost proxy) { this(target, null, proxy, false); } @Override public HttpHost getTargetHost() { return this.targetHost; } @Override public InetAddress getLocalAddress() { return this.localAddress; } public InetSocketAddress getLocalSocketAddress() { return this.localAddress != null ? new InetSocketAddress(this.localAddress, 0) : null; } @Override public int getHopCount() { return proxyChain != null ? proxyChain.size() + 1 : 1; } @Override public HttpHost getHopTarget(final int hop) { Args.notNegative(hop, "Hop index"); final int hopcount = getHopCount(); Args.check(hop < hopcount, "Hop index exceeds tracked route length"); if (hop < hopcount - 1) { return this.proxyChain.get(hop); } return this.targetHost; } @Override public HttpHost getProxyHost() { return proxyChain != null && !this.proxyChain.isEmpty() ? this.proxyChain.get(0) : null; } @Override public TunnelType getTunnelType() { return this.tunnelled; } @Override public boolean isTunnelled() { return (this.tunnelled == TunnelType.TUNNELLED); } @Override public LayerType getLayerType() { return this.layered; } @Override public boolean isLayered() { return (this.layered == LayerType.LAYERED); } @Override public boolean isSecure() { return this.secure; } /** * Compares this route to another. * * @param obj the object to compare with * * @return {@code true} if the argument is the same route, * {@code false} */ @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof HttpRoute) { final HttpRoute that = (HttpRoute) obj; return // Do the cheapest tests first (this.secure == that.secure) && (this.tunnelled == that.tunnelled) && (this.layered == that.layered) && Objects.equals(this.targetHost, that.targetHost) && Objects.equals(this.localAddress, that.localAddress) && Objects.equals(this.proxyChain, that.proxyChain); } return false; } /** * Generates a hash code for this route. * * @return the hash code */ @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.targetHost); hash = LangUtils.hashCode(hash, this.localAddress); if (this.proxyChain != null) { for (final HttpHost element : this.proxyChain) { hash = LangUtils.hashCode(hash, element); } } hash = LangUtils.hashCode(hash, this.secure); hash = LangUtils.hashCode(hash, this.tunnelled); hash = LangUtils.hashCode(hash, this.layered); return hash; } /** * Obtains a description of this route. * * @return a human-readable representation of this route */ @Override public String toString() { final StringBuilder cab = new StringBuilder(50 + getHopCount()*30); if (this.localAddress != null) { cab.append(this.localAddress); cab.append("->"); } cab.append('{'); if (this.tunnelled == TunnelType.TUNNELLED) { cab.append('t'); } if (this.layered == LayerType.LAYERED) { cab.append('l'); } if (this.secure) { cab.append('s'); } cab.append("}->"); if (this.proxyChain != null) { for (final HttpHost aProxyChain : this.proxyChain) { cab.append(aProxyChain); cab.append("->"); } } cab.append(this.targetHost); return cab.toString(); } // default implementation of clone() is sufficient @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } RedirectException.java000066400000000000000000000044121434266521000350610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import org.apache.hc.core5.http.ProtocolException; /** * Signals violation of HTTP specification caused by an invalid redirect * * @since 4.0 */ public class RedirectException extends ProtocolException { private static final long serialVersionUID = 4418824536372559326L; /** * Creates a new RedirectException with a {@code null} detail message. */ public RedirectException() { super(); } /** * Creates a new RedirectException with the specified detail message. * * @param message The exception detail message */ public RedirectException(final String message) { super(message); } /** * Creates a new RedirectException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public RedirectException(final String message, final Throwable cause) { super(message, cause); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/RouteInfo.java000066400000000000000000000117341434266521000334370ustar00rootroot00000000000000/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.net.InetAddress; import org.apache.hc.core5.http.HttpHost; /** * Connection route information. * * @since 4.0 */ public interface RouteInfo { /** * The tunnelling type of a route. * Plain routes are established by connecting to the target or * the first proxy. * Tunnelled routes are established by connecting to the first proxy * and tunnelling through all proxies to the target. * Routes without a proxy cannot be tunnelled. */ enum TunnelType { PLAIN, TUNNELLED } /** * The layering type of a route. * Plain routes are established by connecting or tunnelling. * Layered routes are established by layering a protocol such as TLS/SSL * over an existing connection. * Protocols can only be layered over a tunnel to the target, or * or over a direct connection without proxies. *

* Layering a protocol * over a direct connection makes little sense, since the connection * could be established with the new protocol in the first place. * But we don't want to exclude that use case. *

*/ enum LayerType { PLAIN, LAYERED } /** * Obtains the target host. * * @return the target host */ HttpHost getTargetHost(); /** * Obtains the local address to connect from. * * @return the local address, * or {@code null} */ InetAddress getLocalAddress(); /** * Obtains the number of hops in this route. * A direct route has one hop. A route through a proxy has two hops. * A route through a chain of n proxies has n+1 hops. * * @return the number of hops in this route */ int getHopCount(); /** * Obtains the target of a hop in this route. * The target of the last hop is the {@link #getTargetHost target host}, * the target of previous hops is the respective proxy in the chain. * For a route through exactly one proxy, target of hop 0 is the proxy * and target of hop 1 is the target host. * * @param hop index of the hop for which to get the target, * 0 for first * * @return the target of the given hop * * @throws IllegalArgumentException * if the argument is negative or not less than * {@link #getHopCount getHopCount()} */ HttpHost getHopTarget(int hop); /** * Obtains the first proxy host. * * @return the first proxy in the proxy chain, or * {@code null} if this route is direct */ HttpHost getProxyHost(); /** * Obtains the tunnel type of this route. * If there is a proxy chain, only end-to-end tunnels are considered. * * @return the tunnelling type */ TunnelType getTunnelType(); /** * Checks whether this route is tunnelled through a proxy. * If there is a proxy chain, only end-to-end tunnels are considered. * * @return {@code true} if tunnelled end-to-end through at least * one proxy, * {@code false} otherwise */ boolean isTunnelled(); /** * Obtains the layering type of this route. * In the presence of proxies, only layering over an end-to-end tunnel * is considered. * * @return the layering type */ LayerType getLayerType(); /** * Checks whether this route includes a layered protocol. * In the presence of proxies, only layering over an end-to-end tunnel * is considered. * * @return {@code true} if layered, * {@code false} otherwise */ boolean isLayered(); /** * Checks whether this route is secure. * * @return {@code true} if secure, * {@code false} otherwise */ boolean isSecure(); } RouteTracker.java000066400000000000000000000266041434266521000340620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.net.InetAddress; import java.util.Objects; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.LangUtils; /** * Helps tracking the steps in establishing a route. * * @since 4.0 */ public final class RouteTracker implements RouteInfo, Cloneable { /** The target host to connect to. */ private final HttpHost targetHost; /** * The local address to connect from. * {@code null} indicates that the default should be used. */ private final InetAddress localAddress; // the attributes above are fixed at construction time // now follow attributes that indicate the established route /** Whether the first hop of the route is established. */ private boolean connected; /** The proxy chain, if any. */ private HttpHost[] proxyChain; /** Whether the the route is tunnelled end-to-end through proxies. */ private TunnelType tunnelled; /** Whether the route is layered over a tunnel. */ private LayerType layered; /** Whether the route is secure. */ private boolean secure; /** * Creates a new route tracker. * The target and origin need to be specified at creation time. * * @param target the host to which to route * @param local the local address to route from, or * {@code null} for the default */ public RouteTracker(final HttpHost target, final InetAddress local) { Args.notNull(target, "Target host"); this.targetHost = target; this.localAddress = local; this.tunnelled = TunnelType.PLAIN; this.layered = LayerType.PLAIN; } /** * @since 4.2 */ public void reset() { this.connected = false; this.proxyChain = null; this.tunnelled = TunnelType.PLAIN; this.layered = LayerType.PLAIN; this.secure = false; } /** * Creates a new tracker for the given route. * Only target and origin are taken from the route, * everything else remains to be tracked. * * @param route the route to track */ public RouteTracker(final HttpRoute route) { this(route.getTargetHost(), route.getLocalAddress()); } /** * Tracks connecting to the target. * * @param secure {@code true} if the route is secure, * {@code false} otherwise */ public void connectTarget(final boolean secure) { Asserts.check(!this.connected, "Already connected"); this.connected = true; this.secure = secure; } /** * Tracks connecting to the first proxy. * * @param proxy the proxy connected to * @param secure {@code true} if the route is secure, * {@code false} otherwise */ public void connectProxy(final HttpHost proxy, final boolean secure) { Args.notNull(proxy, "Proxy host"); Asserts.check(!this.connected, "Already connected"); this.connected = true; this.proxyChain = new HttpHost[]{ proxy }; this.secure = secure; } /** * Tracks tunnelling to the target. * * @param secure {@code true} if the route is secure, * {@code false} otherwise */ public void tunnelTarget(final boolean secure) { Asserts.check(this.connected, "No tunnel unless connected"); Asserts.notNull(this.proxyChain, "No tunnel without proxy"); this.tunnelled = TunnelType.TUNNELLED; this.secure = secure; } /** * Tracks tunnelling to a proxy in a proxy chain. * This will extend the tracked proxy chain, but it does not mark * the route as tunnelled. Only end-to-end tunnels are considered there. * * @param proxy the proxy tunnelled to * @param secure {@code true} if the route is secure, * {@code false} otherwise */ public void tunnelProxy(final HttpHost proxy, final boolean secure) { Args.notNull(proxy, "Proxy host"); Asserts.check(this.connected, "No tunnel unless connected"); Asserts.notNull(this.proxyChain, "No tunnel without proxy"); // prepare an extended proxy chain final HttpHost[] proxies = new HttpHost[this.proxyChain.length+1]; System.arraycopy(this.proxyChain, 0, proxies, 0, this.proxyChain.length); proxies[proxies.length-1] = proxy; this.proxyChain = proxies; this.secure = secure; } /** * Tracks layering a protocol. * * @param secure {@code true} if the route is secure, * {@code false} otherwise */ public void layerProtocol(final boolean secure) { // it is possible to layer a protocol over a direct connection, // although this case is probably not considered elsewhere Asserts.check(this.connected, "No layered protocol unless connected"); this.layered = LayerType.LAYERED; this.secure = secure; } @Override public HttpHost getTargetHost() { return this.targetHost; } @Override public InetAddress getLocalAddress() { return this.localAddress; } @Override public int getHopCount() { int hops = 0; if (this.connected) { if (proxyChain == null) { hops = 1; } else { hops = proxyChain.length + 1; } } return hops; } @Override public HttpHost getHopTarget(final int hop) { Args.notNegative(hop, "Hop index"); final int hopcount = getHopCount(); Args.check(hop < hopcount, "Hop index exceeds tracked route length"); HttpHost result = null; if (hop < hopcount-1) { result = this.proxyChain[hop]; } else { result = this.targetHost; } return result; } @Override public HttpHost getProxyHost() { return (this.proxyChain == null) ? null : this.proxyChain[0]; } public boolean isConnected() { return this.connected; } @Override public TunnelType getTunnelType() { return this.tunnelled; } @Override public boolean isTunnelled() { return (this.tunnelled == TunnelType.TUNNELLED); } @Override public LayerType getLayerType() { return this.layered; } @Override public boolean isLayered() { return (this.layered == LayerType.LAYERED); } @Override public boolean isSecure() { return this.secure; } /** * Obtains the tracked route. * If a route has been tracked, it is {@link #isConnected connected}. * If not connected, nothing has been tracked so far. * * @return the tracked route, or * {@code null} if nothing has been tracked so far */ public HttpRoute toRoute() { return !this.connected ? null : new HttpRoute(this.targetHost, this.localAddress, this.proxyChain, this.secure, this.tunnelled, this.layered); } /** * Compares this tracked route to another. * * @param o the object to compare with * * @return {@code true} if the argument is the same tracked route, * {@code false} */ @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof RouteTracker)) { return false; } final RouteTracker that = (RouteTracker) o; return // Do the cheapest checks first (this.connected == that.connected) && (this.secure == that.secure) && (this.tunnelled == that.tunnelled) && (this.layered == that.layered) && Objects.equals(this.targetHost, that.targetHost) && Objects.equals(this.localAddress, that.localAddress) && Objects.equals(this.proxyChain, that.proxyChain); } /** * Generates a hash code for this tracked route. * Route trackers are modifiable and should therefore not be used * as lookup keys. Use {@link #toRoute toRoute} to obtain an * unmodifiable representation of the tracked route. * * @return the hash code */ @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.targetHost); hash = LangUtils.hashCode(hash, this.localAddress); if (this.proxyChain != null) { for (final HttpHost element : this.proxyChain) { hash = LangUtils.hashCode(hash, element); } } hash = LangUtils.hashCode(hash, this.connected); hash = LangUtils.hashCode(hash, this.secure); hash = LangUtils.hashCode(hash, this.tunnelled); hash = LangUtils.hashCode(hash, this.layered); return hash; } /** * Obtains a description of the tracked route. * * @return a human-readable representation of the tracked route */ @Override public String toString() { final StringBuilder cab = new StringBuilder(50 + getHopCount()*30); cab.append("RouteTracker["); if (this.localAddress != null) { cab.append(this.localAddress); cab.append("->"); } cab.append('{'); if (this.connected) { cab.append('c'); } if (this.tunnelled == TunnelType.TUNNELLED) { cab.append('t'); } if (this.layered == LayerType.LAYERED) { cab.append('l'); } if (this.secure) { cab.append('s'); } cab.append("}->"); if (this.proxyChain != null) { for (final HttpHost element : this.proxyChain) { cab.append(element); cab.append("->"); } } cab.append(this.targetHost); cab.append(']'); return cab.toString(); } // default implementation of clone() is sufficient @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } SchemePortResolver.java000066400000000000000000000036501434266521000352370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.net.NamedEndpoint; /** * Strategy for default port resolution for protocol schemes. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface SchemePortResolver { /** * Returns the actual port for the host based on the protocol scheme. */ int resolve(HttpHost host); /** * Returns the actual port for the host based on the protocol scheme. * * @since 5.2 */ default int resolve(String scheme, NamedEndpoint endpoint) { return resolve(new HttpHost(scheme, endpoint)); } } SystemDefaultDnsResolver.java000066400000000000000000000041331434266521000364210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.net.InetAddress; import java.net.UnknownHostException; /** * DNS resolver that uses the default OS implementation for resolving host names. * * @since 4.2 */ public class SystemDefaultDnsResolver implements DnsResolver { public static final SystemDefaultDnsResolver INSTANCE = new SystemDefaultDnsResolver(); @Override public InetAddress[] resolve(final String host) throws UnknownHostException { return InetAddress.getAllByName(host); } @Override public String resolveCanonicalHostname(final String host) throws UnknownHostException { if (host == null) { return null; } final InetAddress in = InetAddress.getByName(host); final String canonicalServer = in.getCanonicalHostName(); if (in.getHostAddress().contentEquals(canonicalServer)) { return host; } return canonicalServer; } } UnsupportedSchemeException.java000066400000000000000000000032161434266521000367760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.io.IOException; /** * Signals failure to establish connection using an unknown protocol scheme. * * @since 4.3 */ public class UnsupportedSchemeException extends IOException { private static final long serialVersionUID = 3597127619218687636L; /** * Creates a UnsupportedSchemeException with the specified detail message. */ public UnsupportedSchemeException(final String message) { super(message); } } UserTokenHandler.java000066400000000000000000000060211434266521000346540ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * A handler for determining if the given execution context is user specific * or not. The token object returned by this handler is expected to uniquely * identify the current user if the context is user specific or to be * {@code null} if the context does not contain any resources or details * specific to the current user. *

* The user token will be used to ensure that user specific resources will not * be shared with or reused by other users. *

* * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface UserTokenHandler { /** * The token object returned by this method is expected to uniquely * identify the current user if the context is user specific or to be * {@code null} if it is not. * * @param route HTTP route * @param context the execution context * * @return user token that uniquely identifies the user or * {@code null} if the context is not user specific. */ Object getUserToken(HttpRoute route, HttpContext context); /** * The token object returned by this method is expected to uniquely * identify the current user if the context is user specific or to be * {@code null} if it is not. * * @param route HTTP route * @param request HTTP request * @param context the execution context * * @return user token that uniquely identifies the user or * {@code null} if the context is not user specific. * * @since 5.2 */ default Object getUserToken(HttpRoute route, HttpRequest request, HttpContext context) { return getUserToken(route, context); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/000077500000000000000000000000001434266521000317715ustar00rootroot00000000000000AsyncExecCallback.java000066400000000000000000000060031434266521000360530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async; import java.io.IOException; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncDataConsumer; /** * AsyncExecCallback methods represent response processing events * in the client side request execution chain. * * @since 5.0 */ public interface AsyncExecCallback { /** * Triggered to signal receipt of a response message head sent by the server * in response to the request being executed. * * @param response the response message head. * @param entityDetails the response entity details or {@code null} if the response * does not enclose an entity. * @return the data consumer to be used for processing of incoming response message body. */ AsyncDataConsumer handleResponse( HttpResponse response, EntityDetails entityDetails) throws HttpException, IOException; /** * Triggered to signal receipt of an intermediate response message. * * @param response the intermediate response message. */ void handleInformationResponse(HttpResponse response) throws HttpException, IOException; /** * Triggered to signal completion of the message exchange. *

* Implementations of this message are expected to perform resource deallocation * allocated in the course of the request execution and response processing. *

*/ void completed(); /** * Triggered to signal a failure occurred during the message exchange. *

* Implementations of this message are expected to perform resource deallocation * allocated in the course of the request execution and response processing. *

*/ void failed(Exception cause); } AsyncExecChain.java000066400000000000000000000134441434266521000354100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** * Represents a single element in the client side asynchronous request execution chain. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface AsyncExecChain { /** * Request execution scope that includes the unique message exchange ID, * the connection route, the original request message, the execution * context and the internal execution runtime. */ final class Scope { public final String exchangeId; public final HttpRoute route; public final HttpRequest originalRequest; public final CancellableDependency cancellableDependency; public final HttpClientContext clientContext; public final AsyncExecRuntime execRuntime; public final Scheduler scheduler; public final AtomicInteger execCount; /** * @since 5.1 */ public Scope( final String exchangeId, final HttpRoute route, final HttpRequest originalRequest, final CancellableDependency cancellableDependency, final HttpClientContext clientContext, final AsyncExecRuntime execRuntime, final Scheduler scheduler, final AtomicInteger execCount) { this.exchangeId = Args.notBlank(exchangeId, "Exchange id"); this.route = Args.notNull(route, "Route"); this.originalRequest = Args.notNull(originalRequest, "Original request"); this.cancellableDependency = Args.notNull(cancellableDependency, "Dependency"); this.clientContext = clientContext != null ? clientContext : HttpClientContext.create(); this.execRuntime = Args.notNull(execRuntime, "Exec runtime"); this.scheduler = scheduler; this.execCount = execCount != null ? execCount : new AtomicInteger(1); } /** * @deprecated Use {@link Scope#Scope(String, HttpRoute, HttpRequest, CancellableDependency, HttpClientContext, * AsyncExecRuntime, Scheduler, AtomicInteger)} */ @Deprecated public Scope( final String exchangeId, final HttpRoute route, final HttpRequest originalRequest, final CancellableDependency cancellableDependency, final HttpClientContext clientContext, final AsyncExecRuntime execRuntime) { this(exchangeId, route, originalRequest, cancellableDependency, clientContext, execRuntime, null, new AtomicInteger(1)); } } /** * Request execution scheduler * * @since 5.1 */ interface Scheduler { /** * Schedules request re-execution immediately or after a delay. * @param request the actual request. * @param entityProducer the request entity producer or {@code null} if the request * does not enclose an entity. * @param scope the execution scope . * @param asyncExecCallback the execution callback. * @param delay re-execution delay. Can be {@code null} if the request is to be * re-executed immediately. */ void scheduleExecution( HttpRequest request, AsyncEntityProducer entityProducer, AsyncExecChain.Scope scope, AsyncExecCallback asyncExecCallback, TimeValue delay); } /** * Proceeds to the next element in the request execution chain. * * @param request the actual request. * @param entityProducer the request entity producer or {@code null} if the request * does not enclose an entity. * @param scope the execution scope . * @param asyncExecCallback the execution callback. */ void proceed( HttpRequest request, AsyncEntityProducer entityProducer, Scope scope, AsyncExecCallback asyncExecCallback) throws HttpException, IOException; } AsyncExecChainHandler.java000066400000000000000000000053631434266521000367070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; /** * Abstract request execution handler in an asynchronous client side request execution * chain. Handlers can either be a decorator around another element that implements * a cross cutting aspect or a self-contained executor capable of producing a response * for the given request. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface AsyncExecChainHandler { /** * Executes the actual HTTP request. The handler can choose to return * a response message immediately inside the call or asynchronously * at some later point or delegate request execution to the next * element in the execution chain. * * @param request the actual request. * @param entityProducer the request entity producer or {@code null} if the request * does not enclose an entity. * @param scope the execution scope . * @param chain the next element in the request execution chain. * @param asyncExecCallback the execution callback. */ void execute( HttpRequest request, AsyncEntityProducer entityProducer, AsyncExecChain.Scope scope, AsyncExecChain chain, AsyncExecCallback asyncExecCallback) throws HttpException, IOException; } AsyncExecRuntime.java000066400000000000000000000134321434266521000360060ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.util.TimeValue; /** * Execution runtime that provides access to the underlying connection endpoint and helps * manager its life cycle. *

* This interface is considered internal and generally ought not be used or accessed * by custom request exec handlers. *

* * @since 5.0 */ @Internal public interface AsyncExecRuntime { /** * Determines of a connection endpoint has been acquired. * * @return {@code true} if an endpoint has been acquired, {@code false} otherwise. */ boolean isEndpointAcquired(); /** * Initiates operation to acquire a connection endpoint. Endpoints can leased from a pool * or unconnected new endpoint can be created. * * @param id unique operation ID or {@code null}. * @param route the connection route. * @param state the expected connection state. May be {@code null} if connection * can be state-less or its state is irrelevant. * @param context the execution context. * @param callback the result callback. * @return handle that can be used to cancel the operation. */ Cancellable acquireEndpoint( String id, HttpRoute route, Object state, HttpClientContext context, FutureCallback callback); /** * Releases the acquired endpoint potentially making it available for re-use. */ void releaseEndpoint(); /** * Shuts down and discards the acquired endpoint. */ void discardEndpoint(); /** * Determines of there the endpoint is connected to the initial hop (connection target * in case of a direct route or to the first proxy hop in case of a route via a proxy * or multiple proxies). * * @return {@code true} if the endpoint is connected, {@code false} otherwise. */ boolean isEndpointConnected(); /** * Initiates operation to connect the local endpoint to the initial hop (connection * target in case of a direct route or to the first proxy hop in case of a route * via a proxy or multiple proxies). * * @param context the execution context. * @param callback the result callback. * @return handle that can be used to cancel the operation. */ Cancellable connectEndpoint( HttpClientContext context, FutureCallback callback); /** * Upgrades transport security of the active connection by using the TLS security protocol. * * @param context the execution context. */ void upgradeTls(HttpClientContext context); /** * Upgrades transport security of the active connection by using the TLS security protocol. * * @param context the execution context. * * @since 5.2 */ default void upgradeTls(HttpClientContext context, FutureCallback callback) { upgradeTls(context); if (callback != null) { callback.completed(this); } } /** * Validates the connection making sure it can be used to execute requests. * * @return {@code true} if the connection is valid, {@code false}. */ boolean validateConnection(); /** * Initiates a message exchange using the given handler. * * @param id unique operation ID or {@code null}. * @param exchangeHandler the client message handler. * @param context the execution context. */ Cancellable execute( String id, AsyncClientExchangeHandler exchangeHandler, HttpClientContext context); /** * Marks the connection as potentially re-usable for the given period of time * and also marks it as stateful if the state representation is given. * @param state the connection state representation or {@code null} if stateless. * @param validityTime the period of time this connection is valid for. */ void markConnectionReusable(Object state, TimeValue validityTime); /** * Marks the connection as non re-usable. */ void markConnectionNonReusable(); /** * Forks this runtime for parallel execution. * * @return another runtime with the same configuration. */ AsyncExecRuntime fork(); } HttpAsyncClient.java000066400000000000000000000062701434266521000356360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async; import java.util.concurrent.Future; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.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. *

* 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. *

* Please note it may be unsafe to interact with the context instance * while the request is still being executed. * * @param the result type of request execution. * @param requestProducer request producer callback. * @param responseConsumer response consumer callback. * @param pushHandlerFactory the push handler factory. Optional and may be {@code null}. * @param context HTTP context. Optional and may be {@code null}. * @param callback future callback. Optional and may be {@code null}. * @return future representing pending completion of the operation. */ Future execute( AsyncRequestProducer requestProducer, AsyncResponseConsumer responseConsumer, HandlerFactory pushHandlerFactory, HttpContext context, FutureCallback callback); } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/000077500000000000000000000000001434266521000334345ustar00rootroot00000000000000AbstractBinPushConsumer.java000066400000000000000000000063251434266521000407760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.UnsupportedCharsetException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.entity.AbstractBinDataConsumer; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract push response consumer that processes response body data as an octet stream. * * @since 5.0 */ public abstract class AbstractBinPushConsumer extends AbstractBinDataConsumer implements AsyncPushConsumer { /** * Triggered to signal the beginning of response processing. * * @param response the response message head * @param contentType the content type of the response body, * or {@code null} if the response does not enclose a response entity. */ protected abstract void start(HttpRequest promise, HttpResponse response, ContentType contentType) throws HttpException, IOException; @Override public final void consumePromise( final HttpRequest promise, final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { if (entityDetails != null) { final ContentType contentType; try { contentType = ContentType.parse(entityDetails.getContentType()); } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } start(promise, response, contentType != null ? contentType : ContentType.DEFAULT_BINARY); } else { start(promise, response, null); completed(); } } @Override public void failed(final Exception cause) { } }AbstractBinResponseConsumer.java000066400000000000000000000075001434266521000416510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.UnsupportedCharsetException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.entity.AbstractBinDataConsumer; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract response consumer that processes response body data as an octet stream. * * @since 5.0 * * @param response message representation. */ public abstract class AbstractBinResponseConsumer extends AbstractBinDataConsumer implements AsyncResponseConsumer { private volatile FutureCallback resultCallback; /** * Triggered to signal the beginning of response processing. * * @param response the response message head * @param contentType the content type of the response body, * or {@code null} if the response does not enclose a response entity. */ protected abstract void start(HttpResponse response, ContentType contentType) throws HttpException, IOException; /** * Triggered to generate object that represents a result of response message processing. * * @return the result of response processing. */ protected abstract T buildResult(); @Override public void informationResponse( final HttpResponse response, final HttpContext context) throws HttpException, IOException { } @Override public final void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context, final FutureCallback resultCallback) throws HttpException, IOException { this.resultCallback = resultCallback; if (entityDetails != null) { try { final ContentType contentType = ContentType.parse(entityDetails.getContentType()); start(response, contentType != null ? contentType : ContentType.DEFAULT_BINARY); } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } } else { start(response, null); completed(); } } @Override protected final void completed() { resultCallback.completed(buildResult()); } @Override public void failed(final Exception cause) { } }AbstractCharPushConsumer.java000066400000000000000000000067741434266521000411530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.entity.AbstractCharDataConsumer; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract push response consumer that processes response body data as a character stream. * * @since 5.0 */ public abstract class AbstractCharPushConsumer extends AbstractCharDataConsumer implements AsyncPushConsumer { /** * Triggered to signal the beginning of data processing. * * @param response the response message head * @param contentType the content type of the response body, * or {@code null} if the response does not enclose a response entity. */ protected abstract void start(HttpRequest promise, HttpResponse response, ContentType contentType) throws HttpException, IOException; @Override public final void consumePromise( final HttpRequest promise, final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { if (entityDetails != null) { final ContentType contentType; try { contentType = ContentType.parse(entityDetails.getContentType()); } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } Charset charset = contentType != null ? contentType.getCharset() : null; if (charset == null) { charset = StandardCharsets.US_ASCII; } setCharset(charset); start(promise, response, contentType != null ? contentType : ContentType.DEFAULT_TEXT); } else { start(promise, response, null); completed(); } } @Override public void failed(final Exception cause) { } }AbstractCharResponseConsumer.java000066400000000000000000000102411434266521000420120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.entity.AbstractCharDataConsumer; import org.apache.hc.core5.http.protocol.HttpContext; /** * Abstract response consumer that processes response body data as a character stream. * * @since 5.0 * * @param response message representation. */ public abstract class AbstractCharResponseConsumer extends AbstractCharDataConsumer implements AsyncResponseConsumer { private volatile FutureCallback resultCallback; /** * Triggered to signal the beginning of data processing. * * @param response the response message head * @param contentType the content type of the response body, * or {@code null} if the response does not enclose a response entity. */ protected abstract void start(HttpResponse response, ContentType contentType) throws HttpException, IOException; /** * Triggered to generate object that represents a result of response message processing. * * @return the result of response processing. */ protected abstract T buildResult() throws IOException; @Override public void informationResponse( final HttpResponse response, final HttpContext context) throws HttpException, IOException { } @Override public final void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context, final FutureCallback resultCallback) throws HttpException, IOException { this.resultCallback = resultCallback; if (entityDetails != null) { final ContentType contentType; try { contentType = ContentType.parse(entityDetails.getContentType()); } catch (final UnsupportedCharsetException ex) { throw new UnsupportedEncodingException(ex.getMessage()); } Charset charset = contentType != null ? contentType.getCharset() : null; if (charset == null) { charset = StandardCharsets.US_ASCII; } setCharset(charset); start(response, contentType != null ? contentType : ContentType.DEFAULT_TEXT); } else { start(response, null); completed(); } } @Override protected final void completed() throws IOException { resultCallback.completed(buildResult()); } @Override public void failed(final Exception cause) { } }BasicHttpRequests.java000066400000000000000000000142531434266521000376420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.net.URI; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.BasicHttpRequest; /** * Common HTTP methods using {@link BasicHttpRequest} as a HTTP request message representation. * * @since 5.0 * * @deprecated Use {@link org.apache.hc.core5.http.support.BasicRequestBuilder}. */ @Deprecated public final class BasicHttpRequests { /** * Creates a new BasicHttpRequest for the given {@code method} and {@code String} URI. * * @param method A method supported by this class. * @param uri a non-null request string URI. * @return A new BasicHttpRequest. */ public static BasicHttpRequest create(final String method, final String uri) { return create(Method.normalizedValueOf(method), uri); } /** * Creates a new BasicHttpRequest for the given {@code method} and {@code URI}. * * @param method A method supported by this class. * @param uri a non-null request URI. * @return A new BasicHttpRequest. */ public static BasicHttpRequest create(final String method, final URI uri) { return create(Method.normalizedValueOf(method), uri); } public static BasicHttpRequest delete(final String uri) { return delete(URI.create(uri)); } public static BasicHttpRequest delete(final URI uri) { return create(Method.DELETE, uri); } public static BasicHttpRequest delete(final HttpHost host, final String path) { return create(Method.DELETE, host, path); } public static BasicHttpRequest get(final String uri) { return get(URI.create(uri)); } public static BasicHttpRequest get(final URI uri) { return create(Method.GET, uri); } public static BasicHttpRequest get(final HttpHost host, final String path) { return create(Method.GET, host, path); } public static BasicHttpRequest head(final String uri) { return head(URI.create(uri)); } public static BasicHttpRequest head(final URI uri) { return create(Method.HEAD, uri); } public static BasicHttpRequest head(final HttpHost host, final String path) { return create(Method.HEAD, host, path); } public static BasicHttpRequest options(final String uri) { return options(URI.create(uri)); } public static BasicHttpRequest options(final URI uri) { return create(Method.OPTIONS, uri); } public static BasicHttpRequest options(final HttpHost host, final String path) { return create(Method.OPTIONS, host, path); } public static BasicHttpRequest patch(final String uri) { return patch(URI.create(uri)); } public static BasicHttpRequest patch(final URI uri) { return create(Method.PATCH, uri); } public static BasicHttpRequest patch(final HttpHost host, final String path) { return create(Method.PATCH, host, path); } public static BasicHttpRequest post(final String uri) { return post(URI.create(uri)); } public static BasicHttpRequest post(final URI uri) { return create(Method.POST, uri); } public static BasicHttpRequest post(final HttpHost host, final String path) { return create(Method.POST, host, path); } public static BasicHttpRequest put(final String uri) { return put(URI.create(uri)); } public static BasicHttpRequest put(final URI uri) { return create(Method.PUT, uri); } public static BasicHttpRequest put(final HttpHost host, final String path) { return create(Method.PUT, host, path); } public static BasicHttpRequest trace(final String uri) { return trace(URI.create(uri)); } public static BasicHttpRequest trace(final URI uri) { return create(Method.TRACE, uri); } public static BasicHttpRequest trace(final HttpHost host, final String path) { return create(Method.TRACE, host, path); } /** * Creates a request object of the exact subclass of {@link BasicHttpRequest}. * * @param uri a non-null URI String. * @return a new subclass of BasicHttpRequest */ public static BasicHttpRequest create(final Method method, final String uri) { return create(method, URI.create(uri)); } /** * Creates a request object of the exact subclass of {@link BasicHttpRequest}. * * @param uri a non-null URI. * @return a new subclass of BasicHttpRequest */ public static BasicHttpRequest create(final Method method, final URI uri) { return new BasicHttpRequest(method, uri); } /** * Creates a request object of the exact subclass of {@link BasicHttpRequest}. * * @param host HTTP host. * @param path request path. * @return a new subclass of BasicHttpRequest */ public static BasicHttpRequest create(final Method method, final HttpHost host, final String path) { return new BasicHttpRequest(method, host, path); } } ConfigurableHttpRequest.java000066400000000000000000000050201434266521000410260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.net.URI; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.net.URIAuthority; /** * HTTP request message with a custom configuration. * * @since 5.0 */ public class ConfigurableHttpRequest extends BasicHttpRequest implements Configurable { private static final long serialVersionUID = 1L; private RequestConfig requestConfig; public ConfigurableHttpRequest(final String method, final String path) { super(method, path); } public ConfigurableHttpRequest(final String method, final HttpHost host, final String path) { super(method, host, path); } /** * @since 5.1 */ public ConfigurableHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) { super(method, scheme, authority, path); } public ConfigurableHttpRequest(final String method, final URI requestUri) { super(method, requestUri); } @Override public RequestConfig getConfig() { return requestConfig; } public void setConfig(final RequestConfig requestConfig) { this.requestConfig = requestConfig; } } IgnoreCompleteExceptionFutureCallback.java000066400000000000000000000043721434266521000436310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import org.apache.hc.core5.concurrent.FutureCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @since 5.2 */ public class IgnoreCompleteExceptionFutureCallback implements FutureCallback { private final FutureCallback callback; private static final Logger LOG = LoggerFactory.getLogger(IgnoreCompleteExceptionFutureCallback.class); public IgnoreCompleteExceptionFutureCallback(final FutureCallback callback) { super(); this.callback = callback; } @Override public void completed(final T result) { if (callback != null) { try { callback.completed(result); } catch (final Exception ex) { LOG.error(ex.getMessage(), ex); } } } @Override public void failed(final Exception ex) { if (callback != null) { callback.failed(ex); } } @Override public void cancelled() { if (callback != null) { callback.cancelled(); } } } IgnoreCompleteExceptonFutureCallback.java000066400000000000000000000031141434266521000434510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import org.apache.hc.core5.concurrent.FutureCallback; /** * @deprecated Use {@link IgnoreCompleteExceptionFutureCallback} * @since 5.2 */ @Deprecated public class IgnoreCompleteExceptonFutureCallback extends IgnoreCompleteExceptionFutureCallback { public IgnoreCompleteExceptonFutureCallback(final FutureCallback callback) { super(callback); } } SimpleAsyncEntityConsumer.java000066400000000000000000000050361434266521000413640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityConsumer; import org.apache.hc.core5.util.ByteArrayBuffer; final class SimpleAsyncEntityConsumer extends AbstractBinAsyncEntityConsumer { private final ByteArrayBuffer buffer; public SimpleAsyncEntityConsumer() { super(); this.buffer = new ByteArrayBuffer(1024); } @Override protected void streamStart(final ContentType contentType) throws HttpException, IOException { } @Override protected int capacityIncrement() { return Integer.MAX_VALUE; } @Override protected void data(final ByteBuffer src, final boolean endOfStream) throws IOException { if (src == null) { return; } if (src.hasArray()) { buffer.append(src.array(), src.arrayOffset() + src.position(), src.remaining()); } else { while (src.hasRemaining()) { buffer.append(src.get()); } } } @Override protected byte[] generateContent() throws IOException { return buffer.toByteArray(); } @Override public void releaseResources() { buffer.clear(); } } SimpleBody.java000066400000000000000000000072351434266521000362760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * Message body representation as a simple text string or an array of bytes. * * @since 5.0 */ public final class SimpleBody { private final byte[] bodyAsBytes; private final String bodyAsText; private final ContentType contentType; SimpleBody(final byte[] bodyAsBytes, final String bodyAsText, final ContentType contentType) { this.bodyAsBytes = bodyAsBytes; this.bodyAsText = bodyAsText; this.contentType = contentType; } static SimpleBody create(final String body, final ContentType contentType) { Args.notNull(body, "Body"); if (body.length() > 2048) { return new SimpleBody(null, body, contentType); } final Charset charset = (contentType != null ? contentType : ContentType.DEFAULT_TEXT).getCharset(); final byte[] bytes = body.getBytes(charset != null ? charset : StandardCharsets.US_ASCII); return new SimpleBody(bytes, null, contentType); } static SimpleBody create(final byte[] body, final ContentType contentType) { Args.notNull(body, "Body"); return new SimpleBody(body, null, contentType); } public ContentType getContentType() { return contentType; } public byte[] getBodyBytes() { if (bodyAsBytes != null) { return bodyAsBytes; } else if (bodyAsText != null) { final Charset charset = (contentType != null ? contentType : ContentType.DEFAULT_TEXT).getCharset(); return bodyAsText.getBytes(charset != null ? charset : StandardCharsets.US_ASCII); } else { return null; } } public String getBodyText() { if (bodyAsBytes != null) { final Charset charset = (contentType != null ? contentType : ContentType.DEFAULT_TEXT).getCharset(); return new String(bodyAsBytes, charset != null ? charset : StandardCharsets.US_ASCII); } else { return bodyAsText; } } public boolean isText() { return bodyAsText != null; } public boolean isBytes() { return bodyAsBytes != null; } @Override public String toString() { return "SimpleBody{content length=" + (bodyAsBytes != null ? bodyAsBytes.length : "chunked") + ", content type=" + contentType + "}"; } } SimpleHttpRequest.java000066400000000000000000000127071434266521000376710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.net.URI; import java.util.Iterator; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; /** * HTTP request that can enclose a body represented as a simple text string or an array of bytes. *

* IMPORTANT: {@link SimpleHttpRequest}s are intended for simple scenarios where entities inclosed * in requests are known to be small. It is generally recommended to use * {@link org.apache.hc.core5.http.nio.support.AsyncRequestBuilder} and streaming * {@link org.apache.hc.core5.http.nio.AsyncEntityProducer}s. * * @since 5.0 * * @see SimpleBody * @see org.apache.hc.core5.http.nio.support.AsyncRequestBuilder * @see org.apache.hc.core5.http.nio.AsyncEntityProducer */ public final class SimpleHttpRequest extends ConfigurableHttpRequest { private static final long serialVersionUID = 1L; private SimpleBody body; /** * @since 5.1 */ public static SimpleHttpRequest create(final String method, final String uri) { return new SimpleHttpRequest(method, uri); } /** * @since 5.1 */ public static SimpleHttpRequest create(final String method, final URI uri) { return new SimpleHttpRequest(method, uri); } /** * @since 5.1 */ public static SimpleHttpRequest create(final Method method, final URI uri) { return new SimpleHttpRequest(method, uri); } /** * @since 5.1 */ public static SimpleHttpRequest create(final Method method, final HttpHost host, final String path) { return new SimpleHttpRequest(method, host, path); } /** * @since 5.1 */ public static SimpleHttpRequest create(final String method, final String scheme, final URIAuthority authority, final String path) { return new SimpleHttpRequest(method, scheme, authority, path); } /** * @deprecated Use {@link SimpleRequestBuilder} */ @Deprecated public static SimpleHttpRequest copy(final HttpRequest original) { Args.notNull(original, "HTTP request"); final SimpleHttpRequest copy = new SimpleHttpRequest(original.getMethod(), original.getRequestUri()); copy.setVersion(original.getVersion()); for (final Iterator

it = original.headerIterator(); it.hasNext(); ) { copy.addHeader(it.next()); } copy.setScheme(original.getScheme()); copy.setAuthority(original.getAuthority()); return copy; } public SimpleHttpRequest(final String method, final String path) { super(method, path); } public SimpleHttpRequest(final String method, final HttpHost host, final String path) { super(method, host, path); } public SimpleHttpRequest(final String method, final URI requestUri) { super(method, requestUri); } /** * @since 5.1 */ public SimpleHttpRequest(final Method method, final URI requestUri) { this(method.name(), requestUri); } /** * @since 5.1 */ public SimpleHttpRequest(final Method method, final HttpHost host, final String path) { this(method.name(), host, path); } /** * @since 5.1 */ public SimpleHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) { super(method, scheme, authority, path); } public void setBody(final SimpleBody body) { this.body = body; } public void setBody(final byte[] bodyBytes, final ContentType contentType) { this.body = SimpleBody.create(bodyBytes, contentType); } public void setBody(final String bodyText, final ContentType contentType) { this.body = SimpleBody.create(bodyText, contentType); } public SimpleBody getBody() { return body; } public ContentType getContentType() { return body != null ? body.getContentType() : null; } public String getBodyText() { return body != null ? body.getBodyText() : null; } public byte[] getBodyBytes() { return body != null ? body.getBodyBytes() : null; } } SimpleHttpRequests.java000066400000000000000000000141711434266521000400510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.net.URI; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; /** * Common HTTP methods using {@link SimpleHttpRequest} as a HTTP request message representation. * * @since 5.0 * * @deprecated Use {@link SimpleRequestBuilder}. */ @Deprecated public final class SimpleHttpRequests { /** * Creates a new BasicHttpRequest for the given {@code method} and {@code String} URI. * * @param method A method supported by this class. * @param uri a non-null request string URI. * @return A new BasicHttpRequest. */ public static SimpleHttpRequest create(final String method, final String uri) { return create(Method.normalizedValueOf(method), uri); } /** * Creates a new BasicHttpRequest for the given {@code method} and {@code URI}. * * @param method A method supported by this class. * @param uri a non-null request URI. * @return A new BasicHttpRequest. */ public static SimpleHttpRequest create(final String method, final URI uri) { return create(Method.normalizedValueOf(method), uri); } public static SimpleHttpRequest delete(final String uri) { return delete(URI.create(uri)); } public static SimpleHttpRequest delete(final URI uri) { return create(Method.DELETE, uri); } public static SimpleHttpRequest delete(final HttpHost host, final String path) { return create(Method.DELETE, host, path); } public static SimpleHttpRequest get(final String uri) { return get(URI.create(uri)); } public static SimpleHttpRequest get(final URI uri) { return create(Method.GET, uri); } public static SimpleHttpRequest get(final HttpHost host, final String path) { return create(Method.GET, host, path); } public static SimpleHttpRequest head(final String uri) { return head(URI.create(uri)); } public static SimpleHttpRequest head(final URI uri) { return create(Method.HEAD, uri); } public static SimpleHttpRequest head(final HttpHost host, final String path) { return create(Method.HEAD, host, path); } public static SimpleHttpRequest options(final String uri) { return options(URI.create(uri)); } public static SimpleHttpRequest options(final URI uri) { return create(Method.OPTIONS, uri); } public static SimpleHttpRequest options(final HttpHost host, final String path) { return create(Method.OPTIONS, host, path); } public static SimpleHttpRequest patch(final String uri) { return patch(URI.create(uri)); } public static SimpleHttpRequest patch(final URI uri) { return create(Method.PATCH, uri); } public static SimpleHttpRequest patch(final HttpHost host, final String path) { return create(Method.PATCH, host, path); } public static SimpleHttpRequest post(final String uri) { return post(URI.create(uri)); } public static SimpleHttpRequest post(final URI uri) { return create(Method.POST, uri); } public static SimpleHttpRequest post(final HttpHost host, final String path) { return create(Method.POST, host, path); } public static SimpleHttpRequest put(final String uri) { return put(URI.create(uri)); } public static SimpleHttpRequest put(final URI uri) { return create(Method.PUT, uri); } public static SimpleHttpRequest put(final HttpHost host, final String path) { return create(Method.PUT, host, path); } public static SimpleHttpRequest trace(final String uri) { return trace(URI.create(uri)); } public static SimpleHttpRequest trace(final URI uri) { return create(Method.TRACE, uri); } public static SimpleHttpRequest trace(final HttpHost host, final String path) { return create(Method.TRACE, host, path); } /** * Creates a request object of the exact subclass of {@link SimpleHttpRequest}. * * @param uri a non-null URI String. * @return a new subclass of SimpleHttpRequest */ public static SimpleHttpRequest create(final Method method, final String uri) { return create(method, URI.create(uri)); } /** * Creates a request object of the exact subclass of {@link SimpleHttpRequest}. * * @param uri a non-null URI. * @return a new subclass of SimpleHttpRequest */ public static SimpleHttpRequest create(final Method method, final URI uri) { return new SimpleHttpRequest(method, uri); } /** * Creates a request object of the exact subclass of {@link SimpleHttpRequest}. * * @param host HTTP host. * @param path request path. * @return a new subclass of SimpleHttpRequest */ public static SimpleHttpRequest create(final Method method, final HttpHost host, final String path) { return new SimpleHttpRequest(method, host, path); } } SimpleHttpResponse.java000066400000000000000000000110401434266521000400240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.util.Iterator; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.util.Args; /** * HTTP response that can enclose a body represented as a simple text string or an array of bytes. *

* IMPORTANT: {@link SimpleHttpResponse}s are intended for simple scenarios where entities inclosed * in responses are known to be small. It is generally recommended to use streaming * {@link org.apache.hc.core5.http.nio.AsyncResponseConsumer}s, for instance, such as based on * {@link AbstractCharResponseConsumer} or {@link AbstractBinResponseConsumer}. * * @since 5.0 * * @see SimpleBody * @see AbstractCharResponseConsumer * @see AbstractBinResponseConsumer */ public final class SimpleHttpResponse extends BasicHttpResponse { private static final long serialVersionUID = 1L; private SimpleBody body; public SimpleHttpResponse(final int code) { super(code); } public SimpleHttpResponse(final int code, final String reasonPhrase) { super(code, reasonPhrase); } public static SimpleHttpResponse copy(final HttpResponse original) { Args.notNull(original, "HTTP response"); final SimpleHttpResponse copy = new SimpleHttpResponse(original.getCode()); copy.setVersion(original.getVersion()); for (final Iterator

it = original.headerIterator(); it.hasNext(); ) { copy.addHeader(it.next()); } return copy; } public static SimpleHttpResponse create(final int code) { return new SimpleHttpResponse(code); } public static SimpleHttpResponse create(final int code, final String content, final ContentType contentType) { final SimpleHttpResponse response = new SimpleHttpResponse(code); if (content != null) { response.setBody(content, contentType); } return response; } public static SimpleHttpResponse create(final int code, final String content) { return create(code, content, ContentType.TEXT_PLAIN); } public static SimpleHttpResponse create(final int code, final byte[] content, final ContentType contentType) { final SimpleHttpResponse response = new SimpleHttpResponse(code); if (content != null) { response.setBody(content, contentType); } return response; } public static SimpleHttpResponse create(final int code, final byte[] content) { return create(code, content, ContentType.TEXT_PLAIN); } public void setBody(final SimpleBody body) { this.body = body; } public void setBody(final byte[] bodyBytes, final ContentType contentType) { this.body = SimpleBody.create(bodyBytes, contentType); } public void setBody(final String bodyText, final ContentType contentType) { this.body = SimpleBody.create(bodyText, contentType); } public SimpleBody getBody() { return body; } public ContentType getContentType() { return body != null ? body.getContentType() : null; } public String getBodyText() { return body != null ? body.getBodyText() : null; } public byte[] getBodyBytes() { return body != null ? body.getBodyBytes() : null; } } SimpleRequestBuilder.java000066400000000000000000000321501434266521000403320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.support.AbstractRequestBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.net.WWWFormCodec; import org.apache.hc.core5.util.Args; /** * Builder for {@link SimpleHttpRequest} instances. *

* Please note that this class treats parameters differently depending on composition * of the request: if the request has a content entity explicitly set with * {@link #setBody(SimpleBody)} or it is not an entity enclosing method * (such as POST or PUT), parameters will be added to the query component * of the request URI. Otherwise, parameters will be added as a URL encoded entity. *

* * @since 5.0 */ public class SimpleRequestBuilder extends AbstractRequestBuilder { private SimpleBody body; private RequestConfig requestConfig; SimpleRequestBuilder(final String method) { super(method); } SimpleRequestBuilder(final Method method) { super(method); } SimpleRequestBuilder(final String method, final URI uri) { super(method, uri); } SimpleRequestBuilder(final Method method, final URI uri) { super(method, uri); } SimpleRequestBuilder(final Method method, final String uri) { super(method, uri); } SimpleRequestBuilder(final String method, final String uri) { super(method, uri); } public static SimpleRequestBuilder create(final String method) { Args.notBlank(method, "HTTP method"); return new SimpleRequestBuilder(method); } public static SimpleRequestBuilder create(final Method method) { Args.notNull(method, "HTTP method"); return new SimpleRequestBuilder(method); } public static SimpleRequestBuilder get() { return new SimpleRequestBuilder(Method.GET); } public static SimpleRequestBuilder get(final URI uri) { return new SimpleRequestBuilder(Method.GET, uri); } public static SimpleRequestBuilder get(final String uri) { return new SimpleRequestBuilder(Method.GET, uri); } public static SimpleRequestBuilder head() { return new SimpleRequestBuilder(Method.HEAD); } public static SimpleRequestBuilder head(final URI uri) { return new SimpleRequestBuilder(Method.HEAD, uri); } public static SimpleRequestBuilder head(final String uri) { return new SimpleRequestBuilder(Method.HEAD, uri); } public static SimpleRequestBuilder patch() { return new SimpleRequestBuilder(Method.PATCH); } public static SimpleRequestBuilder patch(final URI uri) { return new SimpleRequestBuilder(Method.PATCH, uri); } public static SimpleRequestBuilder patch(final String uri) { return new SimpleRequestBuilder(Method.PATCH, uri); } public static SimpleRequestBuilder post() { return new SimpleRequestBuilder(Method.POST); } public static SimpleRequestBuilder post(final URI uri) { return new SimpleRequestBuilder(Method.POST, uri); } public static SimpleRequestBuilder post(final String uri) { return new SimpleRequestBuilder(Method.POST, uri); } public static SimpleRequestBuilder put() { return new SimpleRequestBuilder(Method.PUT); } public static SimpleRequestBuilder put(final URI uri) { return new SimpleRequestBuilder(Method.PUT, uri); } public static SimpleRequestBuilder put(final String uri) { return new SimpleRequestBuilder(Method.PUT, uri); } public static SimpleRequestBuilder delete() { return new SimpleRequestBuilder(Method.DELETE); } public static SimpleRequestBuilder delete(final URI uri) { return new SimpleRequestBuilder(Method.DELETE, uri); } public static SimpleRequestBuilder delete(final String uri) { return new SimpleRequestBuilder(Method.DELETE, uri); } public static SimpleRequestBuilder trace() { return new SimpleRequestBuilder(Method.TRACE); } public static SimpleRequestBuilder trace(final URI uri) { return new SimpleRequestBuilder(Method.TRACE, uri); } public static SimpleRequestBuilder trace(final String uri) { return new SimpleRequestBuilder(Method.TRACE, uri); } public static SimpleRequestBuilder options() { return new SimpleRequestBuilder(Method.OPTIONS); } public static SimpleRequestBuilder options(final URI uri) { return new SimpleRequestBuilder(Method.OPTIONS, uri); } public static SimpleRequestBuilder options(final String uri) { return new SimpleRequestBuilder(Method.OPTIONS, uri); } /** * @since 5.1 */ public static SimpleRequestBuilder copy(final SimpleHttpRequest request) { Args.notNull(request, "HTTP request"); final SimpleRequestBuilder builder = new SimpleRequestBuilder(request.getMethod()); builder.digest(request); return builder; } /** * @since 5.1 */ public static SimpleRequestBuilder copy(final HttpRequest request) { Args.notNull(request, "HTTP request"); final SimpleRequestBuilder builder = new SimpleRequestBuilder(request.getMethod()); builder.digest(request); return builder; } protected void digest(final SimpleHttpRequest request) { super.digest(request); setBody(request.getBody()); } @Override protected void digest(final HttpRequest request) { super.digest(request); } @Override public SimpleRequestBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public SimpleRequestBuilder setUri(final URI uri) { super.setUri(uri); return this; } @Override public SimpleRequestBuilder setUri(final String uri) { super.setUri(uri); return this; } @Override public SimpleRequestBuilder setScheme(final String scheme) { super.setScheme(scheme); return this; } @Override public SimpleRequestBuilder setAuthority(final URIAuthority authority) { super.setAuthority(authority); return this; } @Override public SimpleRequestBuilder setHttpHost(final HttpHost httpHost) { super.setHttpHost(httpHost); return this; } @Override public SimpleRequestBuilder setPath(final String path) { super.setPath(path); return this; } @Override public SimpleRequestBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public SimpleRequestBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public SimpleRequestBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public SimpleRequestBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public SimpleRequestBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public SimpleRequestBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public SimpleRequestBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } @Override public SimpleRequestBuilder setCharset(final Charset charset) { super.setCharset(charset); return this; } @Override public SimpleRequestBuilder addParameter(final NameValuePair nvp) { super.addParameter(nvp); return this; } @Override public SimpleRequestBuilder addParameter(final String name, final String value) { super.addParameter(name, value); return this; } @Override public SimpleRequestBuilder addParameters(final NameValuePair... nvps) { super.addParameters(nvps); return this; } @Override public SimpleRequestBuilder setAbsoluteRequestUri(final boolean absoluteRequestUri) { super.setAbsoluteRequestUri(absoluteRequestUri); return this; } public SimpleBody getBody() { return body; } public SimpleRequestBuilder setBody(final SimpleBody body) { this.body = body; return this; } public SimpleRequestBuilder setBody(final String content, final ContentType contentType) { this.body = SimpleBody.create(content, contentType); return this; } public SimpleRequestBuilder setBody(final byte[] content, final ContentType contentType) { this.body = SimpleBody.create(content, contentType); return this; } public RequestConfig getRequestConfig() { return requestConfig; } public SimpleRequestBuilder setRequestConfig(final RequestConfig requestConfig) { this.requestConfig = requestConfig; return this; } @Override public SimpleHttpRequest build() { String path = getPath(); SimpleBody bodyCopy = this.body; final String method = getMethod(); final List parameters = getParameters(); if (parameters != null && !parameters.isEmpty()) { final Charset charsetCopy = getCharset(); if (bodyCopy == null && (Method.POST.isSame(method) || Method.PUT.isSame(method))) { final String content = WWWFormCodec.format( parameters, charsetCopy != null ? charsetCopy : ContentType.APPLICATION_FORM_URLENCODED.getCharset()); bodyCopy = SimpleBody.create(content, ContentType.APPLICATION_FORM_URLENCODED); } else { try { final URI uri = new URIBuilder(path) .setCharset(charsetCopy) .addParameters(parameters) .build(); path = uri.toASCIIString(); } catch (final URISyntaxException ex) { // should never happen } } } if (bodyCopy != null && Method.TRACE.isSame(method)) { throw new IllegalStateException(Method.TRACE + " requests may not include an entity"); } final SimpleHttpRequest result = new SimpleHttpRequest(method, getScheme(), getAuthority(), path); result.setVersion(getVersion()); result.setHeaders(getHeaders()); result.setBody(bodyCopy); result.setAbsoluteRequestUri(isAbsoluteRequestUri()); result.setConfig(requestConfig); return result; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ClassicRequestBuilder [method="); builder.append(getMethod()); builder.append(", scheme="); builder.append(getScheme()); builder.append(", authority="); builder.append(getAuthority()); builder.append(", path="); builder.append(getPath()); builder.append(", parameters="); builder.append(getParameters()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append(", body="); builder.append(body); builder.append("]"); return builder.toString(); } } SimpleRequestProducer.java000066400000000000000000000060061434266521000405300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.util.Args; /** * HTTP request producer that generates message data stream events based * on content of a {@link SimpleHttpRequest} instance. *

* IMPORTANT: {@link SimpleHttpRequest}s are intended for simple scenarios where entities inclosed * in requests are known to be small. It is generally recommended to use * {@link org.apache.hc.core5.http.nio.support.AsyncRequestBuilder} and streaming * {@link org.apache.hc.core5.http.nio.AsyncEntityProducer}s. * * @since 5.0 * * @see SimpleBody * @see SimpleHttpRequest * @see org.apache.hc.core5.http.nio.support.AsyncRequestBuilder * @see org.apache.hc.core5.http.nio.AsyncEntityProducer */ public final class SimpleRequestProducer extends BasicRequestProducer { SimpleRequestProducer(final SimpleHttpRequest request, final AsyncEntityProducer entityProducer) { super(request, entityProducer); } public static SimpleRequestProducer create(final SimpleHttpRequest request) { Args.notNull(request, "Request"); final SimpleBody body = request.getBody(); final AsyncEntityProducer entityProducer; if (body != null) { if (body.isText()) { entityProducer = new StringAsyncEntityProducer(body.getBodyText(), body.getContentType()); } else { entityProducer = new BasicAsyncEntityProducer(body.getBodyBytes(), body.getContentType()); } } else { entityProducer = null; } return new SimpleRequestProducer(request, entityProducer); } } SimpleResponseBuilder.java000066400000000000000000000113711434266521000405020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.util.Arrays; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.support.AbstractResponseBuilder; import org.apache.hc.core5.util.Args; /** * Builder for {@link SimpleHttpResponse} instances. * * @since 5.1 */ public class SimpleResponseBuilder extends AbstractResponseBuilder { private SimpleBody body; SimpleResponseBuilder(final int status) { super(status); } public static SimpleResponseBuilder create(final int status) { Args.checkRange(status, 100, 599, "HTTP status code"); return new SimpleResponseBuilder(status); } public static SimpleResponseBuilder copy(final SimpleHttpResponse response) { Args.notNull(response, "HTTP response"); final SimpleResponseBuilder builder = new SimpleResponseBuilder(response.getCode()); builder.digest(response); return builder; } protected void digest(final SimpleHttpResponse response) { super.digest(response); setBody(response.getBody()); } @Override public SimpleResponseBuilder setVersion(final ProtocolVersion version) { super.setVersion(version); return this; } @Override public SimpleResponseBuilder setHeaders(final Header... headers) { super.setHeaders(headers); return this; } @Override public SimpleResponseBuilder addHeader(final Header header) { super.addHeader(header); return this; } @Override public SimpleResponseBuilder addHeader(final String name, final String value) { super.addHeader(name, value); return this; } @Override public SimpleResponseBuilder removeHeader(final Header header) { super.removeHeader(header); return this; } @Override public SimpleResponseBuilder removeHeaders(final String name) { super.removeHeaders(name); return this; } @Override public SimpleResponseBuilder setHeader(final Header header) { super.setHeader(header); return this; } @Override public SimpleResponseBuilder setHeader(final String name, final String value) { super.setHeader(name, value); return this; } public SimpleBody getBody() { return body; } public SimpleResponseBuilder setBody(final SimpleBody body) { this.body = body; return this; } public SimpleResponseBuilder setBody(final String content, final ContentType contentType) { this.body = SimpleBody.create(content, contentType); return this; } public SimpleResponseBuilder setBody(final byte[] content, final ContentType contentType) { this.body = SimpleBody.create(content, contentType); return this; } @Override public SimpleHttpResponse build() { final SimpleHttpResponse result = new SimpleHttpResponse(getStatus()); result.setVersion(getVersion()); result.setHeaders(getHeaders()); result.setBody(body); return result; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("SimpleResponseBuilder [status="); builder.append(getStatus()); builder.append(", headerGroup="); builder.append(Arrays.toString(getHeaders())); builder.append(", body="); builder.append(body); builder.append("]"); return builder.toString(); } } SimpleResponseConsumer.java000066400000000000000000000060371434266521000407120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import java.io.IOException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.AbstractAsyncResponseConsumer; import org.apache.hc.core5.http.protocol.HttpContext; /** * HTTP response consumer that generates a {@link SimpleHttpResponse} instance based on events * of an incoming data stream. *

* IMPORTANT: {@link SimpleHttpResponse}s are intended for simple scenarios where entities inclosed * in responses are known to be small. It is generally recommended to use streaming * {@link org.apache.hc.core5.http.nio.AsyncResponseConsumer}s, for instance, such as based on * {@link AbstractCharResponseConsumer} or {@link AbstractBinResponseConsumer}. * * @since 5.0 * * @see SimpleBody * @see SimpleHttpResponse * @see AbstractCharResponseConsumer * @see AbstractBinResponseConsumer */ public final class SimpleResponseConsumer extends AbstractAsyncResponseConsumer { SimpleResponseConsumer(final AsyncEntityConsumer entityConsumer) { super(entityConsumer); } public static SimpleResponseConsumer create() { return new SimpleResponseConsumer(new SimpleAsyncEntityConsumer()); } @Override public void informationResponse(final HttpResponse response, final HttpContext context) throws HttpException, IOException { } @Override protected SimpleHttpResponse buildResult(final HttpResponse response, final byte[] entity, final ContentType contentType) { final SimpleHttpResponse simpleResponse = SimpleHttpResponse.copy(response); if (entity != null) { simpleResponse.setBody(entity, contentType); } return simpleResponse; } }package-info.java000066400000000000000000000024471434266521000365530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Common HTTP methods and message handlers for the asynchronous I/O model. */ package org.apache.hc.client5.http.async.methods; package-info.java000066400000000000000000000024321434266521000351020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP client APIs based on the asynchronous, event driven I/O model. */ package org.apache.hc.client5.http.async; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/000077500000000000000000000000001434266521000316155ustar00rootroot00000000000000AuthCache.java000066400000000000000000000072651434266521000342400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import org.apache.hc.core5.http.HttpHost; /** * This interface represents an cache of {@link AuthScheme} state information * that can be re-used for preemptive authentication by subsequent requests. * * @since 4.1 */ public interface AuthCache { /** * Stores the authentication state with the given authentication scope in the cache. * * @param host the authentication authority. * @param authScheme the cacheable authentication state. */ void put(HttpHost host, AuthScheme authScheme); /** * Returns the authentication state with the given authentication scope from the cache * if available. * * @param host the authentication authority. * @return the authentication state ir {@code null} if not available in the cache. */ AuthScheme get(HttpHost host); /** * Removes the authentication state with the given authentication scope from the cache * if found. * * @param host the authentication authority. */ void remove(HttpHost host); void clear(); /** * Stores the authentication state with the given authentication scope in the cache. * * @param host the authentication authority. * @param pathPrefix the path prefix (the path component up to the last segment separator). * Can be {@code null}. * @param authScheme the cacheable authentication state. * * @since 5.2 */ default void put(HttpHost host, String pathPrefix, AuthScheme authScheme) { put(host, authScheme); } /** * Returns the authentication state with the given authentication scope from the cache * if available. * @param host the authentication authority. * @param pathPrefix the path prefix (the path component up to the last segment separator). * Can be {@code null}. * @return the authentication state ir {@code null} if not available in the cache. * * @since 5.2 */ default AuthScheme get(HttpHost host, String pathPrefix) { return get(host); } /** * Removes the authentication state with the given authentication scope from the cache * if found. * * @param host the authentication authority. * @param pathPrefix the path prefix (the path component up to the last segment separator). * Can be {@code null}. * * @since 5.2 */ default void remove(HttpHost host, String pathPrefix) { remove(host); } } AuthChallenge.java000066400000000000000000000062161434266521000351120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.Args; /** * This class represents an authentication challenge consisting of a auth scheme * and either a single parameter or a list of name / value pairs. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class AuthChallenge { private final ChallengeType challengeType; private final String schemeName; private final String value; private final List params; public AuthChallenge(final ChallengeType challengeType, final String schemeName, final String value, final List params) { super(); this.challengeType = Args.notNull(challengeType, "Challenge type"); this.schemeName = Args.notNull(schemeName, "schemeName"); this.value = value; this.params = params != null ? Collections.unmodifiableList(new ArrayList<>(params)) : null; } public AuthChallenge(final ChallengeType challengeType, final String schemeName, final NameValuePair... params) { this(challengeType, schemeName, null, Arrays.asList(params)); } public ChallengeType getChallengeType() { return challengeType; } public String getSchemeName() { return schemeName; } public String getValue() { return value; } public List getParams() { return params; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append(schemeName).append(" "); if (value != null) { buffer.append(value); } else if (params != null) { buffer.append(params); } return buffer.toString(); } } AuthExchange.java000066400000000000000000000076471434266521000347630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.util.Queue; import org.apache.hc.core5.util.Args; /** * This class represents the actual state of authentication handshake including the current {@link AuthScheme} * used for request authorization as well as a collection of backup authentication options if available. * * @since 4.5 */ public class AuthExchange { public enum State { UNCHALLENGED, CHALLENGED, HANDSHAKE, FAILURE, SUCCESS } private State state; private AuthScheme authScheme; private Queue authOptions; private String pathPrefix; public AuthExchange() { super(); this.state = State.UNCHALLENGED; } public void reset() { this.state = State.UNCHALLENGED; this.authOptions = null; this.authScheme = null; this.pathPrefix = null; } public State getState() { return this.state; } public void setState(final State state) { this.state = state != null ? state : State.UNCHALLENGED; } /** * Returns actual {@link AuthScheme}. May be null. */ public AuthScheme getAuthScheme() { return this.authScheme; } /** * Returns {@code true} if the actual authentication scheme is connection based. */ public boolean isConnectionBased() { return this.authScheme != null && this.authScheme.isConnectionBased(); } /** * @since 5.2 */ public String getPathPrefix() { return pathPrefix; } /** * @since 5.2 */ public void setPathPrefix(final String pathPrefix) { this.pathPrefix = pathPrefix; } /** * Resets the auth state with {@link AuthScheme} and clears auth options. * * @param authScheme auth scheme. May not be null. */ public void select(final AuthScheme authScheme) { Args.notNull(authScheme, "Auth scheme"); this.authScheme = authScheme; this.authOptions = null; } /** * Returns available auth options. May be null. */ public Queue getAuthOptions() { return this.authOptions; } /** * Updates the auth state with a queue of auth options. * * @param authOptions a queue of auth options. May not be null or empty. */ public void setOptions(final Queue authOptions) { Args.notEmpty(authOptions, "Queue of auth options"); this.authOptions = authOptions; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[").append(this.state); if (this.authScheme != null) { buffer.append(" ").append(this.authScheme); } buffer.append("]"); return buffer.toString(); } } AuthScheme.java000066400000000000000000000205151434266521000344320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.security.Principal; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * This interface represents an abstract challenge-response oriented authentication scheme. *

* Authentication schemes can be either request or connection based. The former are * expected to provide an authorization response with each request message while the latter * is executed only once and applies to the underlying connection for its entire life span. * Care must be taken when re-using connections authorized through a connection based * authentication scheme and they may carry a particular security context and be authorized * for a particular user identity. It is important that such schemes always provide * the user identity they represent through the {@link #getPrincipal()} method. *

*

* Authentication scheme are expected to transition through a series of standard phases or * states. *

*

* Authentication scheme starts off its life cycle with no context and no specific state. *

*

* The {@link #processChallenge(AuthChallenge, HttpContext)} method is called to * process an authentication challenge received either from the target server or a proxy. * The authentication scheme transitions to CHALLENGED state and is expected to validate * the token passed to it as a parameter and initialize its internal state based on * challenge details. Standard authentication schemes are expected to provide a realm * attribute in the challenge. {@link #getRealm()} can be called to obtain an identifier * of the realm that requires authorization. *

*

* Once the challenge has been fully processed the {@link #isResponseReady(HttpHost, * CredentialsProvider, HttpContext)} method to determine whether the scheme is capable of * generating a authorization response based on its current state and it holds user credentials * required to do so. If this method returns {@code false} the authentication is considered * to be in FAILED state and no authorization response. Otherwise the scheme is considered * to be in RESPONSE_READY state. *

*

* Once the scheme is ready to respond to the challenge the {@link #generateAuthResponse( * HttpHost, HttpRequest, HttpContext)} method to generate a response token, which will * be sent to the opposite endpoint in the subsequent request message. *

*

* Certain non-standard schemes may involve multiple challenge / response exchanges to * fully establish a shared context and complete the authentication process. Authentication * schemes are required to return {@code true} {@link #isChallengeComplete()} once the * handshake is considered complete. *

*

* The authentication scheme is considered successfully completed and in SUCCESS state * if the opposite endpoint accepts the request message containing the authorization * response and responds with a message indicating no authentication failure . * If the opposite endpoint sends status code 401 or 407 in response to a request message * containing the terminal authorization response, the scheme is considered unsuccessful * and in FAILED state. *

* * @since 4.0 */ public interface AuthScheme { /** * Returns textual designation of the given authentication scheme. * * @return the name of the given authentication scheme */ String getName(); /** * Determines if the authentication scheme is expected to provide an authorization response * on a per connection basis instead of the standard per request basis * * @return {@code true} if the scheme is connection based, {@code false} * if the scheme is request based. */ boolean isConnectionBased(); /** * Processes the given auth challenge. Some authentication schemes may involve multiple * challenge-response exchanges. Such schemes must be able to maintain internal state * when dealing with sequential challenges * * @param authChallenge the auth challenge * @param context HTTP context * @throws MalformedChallengeException in case the auth challenge is incomplete, * malformed or otherwise invalid. * @since 5.0 */ void processChallenge( AuthChallenge authChallenge, HttpContext context) throws MalformedChallengeException; /** * Authentication process may involve a series of challenge-response exchanges. * This method tests if the authorization process has been fully completed (either * successfully or unsuccessfully), that is, all the required authorization * challenges have been processed in their entirety. * * @return {@code true} if the authentication process has been completed, * {@code false} otherwise. * * @since 5.0 */ boolean isChallengeComplete(); /** * Returns authentication realm. If the concept of an authentication * realm is not applicable to the given authentication scheme, returns * {@code null}. * * @return the authentication realm */ String getRealm(); /** * Determines whether or not an authorization response can be generated based on * the actual authentication state. Generally the outcome of this method will depend * upon availability of user credentials necessary to produce an authorization * response. * * @param credentialsProvider The credentials to be used for authentication * @param context HTTP context * @throws AuthenticationException if authorization string cannot * be generated due to an authentication failure * * @return {@code true} if an authorization response can be generated and * the authentication handshake can proceed, {@code false} otherwise. * * @since 5.0 */ boolean isResponseReady( HttpHost host, CredentialsProvider credentialsProvider, HttpContext context) throws AuthenticationException; /** * Returns {@link Principal} whose credentials are used to generate * an authentication response. Connection based schemes are required * to return a user {@link Principal} if authorization applies to * for the entire life span of connection. * @return user principal * * @see #isConnectionBased() * * @since 5.0 */ Principal getPrincipal(); /** * Generates an authorization response based on the current state. Some authentication * schemes may need to load user credentials required to generate an authorization * response from a {@link CredentialsProvider} prior to this method call. * * @param request The request being authenticated * @param context HTTP context * @throws AuthenticationException if authorization string cannot * be generated due to an authentication failure * * @return authorization header * * @see #isResponseReady(HttpHost, CredentialsProvider, HttpContext) * * @since 5.0 */ String generateAuthResponse( HttpHost host, HttpRequest request, HttpContext context) throws AuthenticationException; } AuthSchemeFactory.java000066400000000000000000000030101434266521000357510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import org.apache.hc.core5.http.protocol.HttpContext; /** * Factory for {@link AuthScheme} implementations. * * @since 4.3 */ public interface AuthSchemeFactory { /** * Creates an instance of {@link AuthScheme}. * * @return auth scheme. */ AuthScheme create(HttpContext context); } AuthScope.java000066400000000000000000000214361434266521000343020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.util.Locale; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; /** * {@code AuthScope} represents an authentication scope consisting of * an application protocol, a host name, a port number, a realm name * and an authentication scheme name. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class AuthScope { private final String protocol; private final String host; private final int port; private final String realm; private final String schemeName; /** * Defines auth scope with the given {@code protocol}, {@code host}, {@code port}, * {@code realm}, and {@code schemeName}. * * @param protocol application protocol. May be {@code null} if applies * to any protocol. * @param host authentication host. May be {@code null} if applies * to any host. * @param port authentication port. May be {@code -1} if applies * to any port of the host. * @param realm authentication realm. May be {@code null} if applies * to any realm on the host. * @param schemeName authentication scheme name. May be {@code null} if applies * to any auth scheme supported by the host. */ public AuthScope( final String protocol, final String host, final int port, final String realm, final String schemeName) { this.protocol = protocol != null ? protocol.toLowerCase(Locale.ROOT) : null; this.host = host != null ? host.toLowerCase(Locale.ROOT) : null; this.port = port >= 0 ? port: -1; this.realm = realm; this.schemeName = schemeName; } /** * Defines auth scope for a specific host of origin. * * @param origin host of origin * @param realm authentication realm. May be {@code null} if applies * to any realm on the host. * @param schemeName authentication scheme name. May be {@code null} if applies * to any auth scheme supported by the host. * * @since 4.2 */ public AuthScope( final HttpHost origin, final String realm, final String schemeName) { Args.notNull(origin, "Host"); this.protocol = origin.getSchemeName().toLowerCase(Locale.ROOT); this.host = origin.getHostName().toLowerCase(Locale.ROOT); this.port = origin.getPort() >= 0 ? origin.getPort() : -1; this.realm = realm; this.schemeName = schemeName; } /** * Defines auth scope for a specific host of origin. * * @param origin host of origin * * @since 4.2 */ public AuthScope(final HttpHost origin) { this(origin, null, null); } /** * Defines auth scope with the given {@code host} and {@code port}. * * @param host authentication host. May be {@code null} if applies * to any host. * @param port authentication port. May be {@code -1} if applies * to any port of the host. */ public AuthScope(final String host, final int port) { this(null, host, port, null, null); } /** * Creates a copy of the given credentials scope. */ public AuthScope(final AuthScope authScope) { super(); Args.notNull(authScope, "Scope"); this.protocol = authScope.getProtocol(); this.host = authScope.getHost(); this.port = authScope.getPort(); this.realm = authScope.getRealm(); this.schemeName = authScope.getSchemeName(); } public String getProtocol() { return protocol; } public String getHost() { return this.host; } public int getPort() { return this.port; } public String getRealm() { return this.realm; } public String getSchemeName() { return this.schemeName; } /** * Tests if the authentication scopes match. * * @return the match factor. Negative value signifies no match. * Non-negative signifies a match. The greater the returned value * the closer the match. */ public int match(final AuthScope that) { int factor = 0; if (Objects.equals(toNullSafeLowerCase(this.schemeName), toNullSafeLowerCase(that.schemeName))) { factor += 1; } else { if (this.schemeName != null && that.schemeName != null) { return -1; } } if (Objects.equals(this.realm, that.realm)) { factor += 2; } else { if (this.realm != null && that.realm != null) { return -1; } } if (this.port == that.port) { factor += 4; } else { if (this.port != -1 && that.port != -1) { return -1; } } if (Objects.equals(this.protocol, that.protocol)) { factor += 8; } else { if (this.protocol != null && that.protocol != null) { return -1; } } if (Objects.equals(this.host, that.host)) { factor += 16; } else { if (this.host != null && that.host != null) { return -1; } } return factor; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof AuthScope) { final AuthScope that = (AuthScope) obj; return Objects.equals(this.protocol, that.protocol) && Objects.equals(this.host, that.host) && this.port == that.port && Objects.equals(this.realm, that.realm) && Objects.equals(toNullSafeLowerCase(this.schemeName), toNullSafeLowerCase(that.schemeName)); } return false; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.protocol); hash = LangUtils.hashCode(hash, this.host); hash = LangUtils.hashCode(hash, this.port); hash = LangUtils.hashCode(hash, this.realm); hash = LangUtils.hashCode(hash, toNullSafeLowerCase(this.schemeName)); return hash; } private String toNullSafeLowerCase(final String str) { return str != null ? str.toLowerCase(Locale.ROOT) : null; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); if (this.schemeName != null) { buffer.append(this.schemeName); } else { buffer.append(""); } buffer.append(' '); if (this.realm != null) { buffer.append('\''); buffer.append(this.realm); buffer.append('\''); } else { buffer.append(""); } buffer.append(' '); if (this.protocol != null) { buffer.append(this.protocol); } else { buffer.append(""); } buffer.append("://"); if (this.host != null) { buffer.append(this.host); } else { buffer.append(""); } buffer.append(':'); if (this.port >= 0) { buffer.append(this.port); } else { buffer.append(""); } return buffer.toString(); } } AuthStateCacheable.java000066400000000000000000000032521434266521000360550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates that the state of an {@link AuthScheme} can be cached * and re-used for preemptive authentication by subsequent requests. * * @since 5.0 */ @Documented @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface AuthStateCacheable { } AuthenticationException.java000066400000000000000000000044321434266521000372420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import org.apache.hc.core5.http.ProtocolException; /** * Signals a failure in authentication process * * @since 4.0 */ public class AuthenticationException extends ProtocolException { private static final long serialVersionUID = -6794031905674764776L; /** * Creates a new AuthenticationException with a {@code null} detail message. */ public AuthenticationException() { super(); } /** * Creates a new AuthenticationException with the specified message. * * @param message the exception detail message */ public AuthenticationException(final String message) { super(message); } /** * Creates a new AuthenticationException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public AuthenticationException(final String message, final Throwable cause) { super(message, cause); } } BasicUserPrincipal.java000066400000000000000000000053661434266521000361350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.io.Serializable; import java.security.Principal; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; /** * Basic username based principal representation. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class BasicUserPrincipal implements Principal, Serializable { private static final long serialVersionUID = -2266305184969850467L; private final String username; public BasicUserPrincipal(final String username) { super(); Args.notNull(username, "User name"); this.username = username; } @Override public String getName() { return this.username; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.username); return hash; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o instanceof BasicUserPrincipal) { final BasicUserPrincipal that = (BasicUserPrincipal) o; return Objects.equals(this.username, that.username); } return false; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[principal: "); buffer.append(this.username); buffer.append("]"); return buffer.toString(); } } ChallengeType.java000066400000000000000000000024711434266521000351310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; /** * Challenge type (TARGET or PROXY) * * @since 4.2 */ public enum ChallengeType { TARGET, PROXY } Credentials.java000066400000000000000000000030031434266521000346320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.security.Principal; /** * This interface represents a set of credentials consisting of a security * principal and a secret (password) that can be used to establish user * identity * * @since 4.0 */ public interface Credentials { Principal getUserPrincipal(); char[] getPassword(); } CredentialsProvider.java000066400000000000000000000036461434266521000363620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import org.apache.hc.core5.http.protocol.HttpContext; /** * Provider of authentication credentials. *

* 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 CredentialsProvider { /** * Returns {@link Credentials credentials} for the given authentication scope, * if available. * * @param authScope the {@link AuthScope authentication scope} * @param context the {@link HttpContext http context} * @return the credentials * @since 5.0 */ Credentials getCredentials(AuthScope authScope, HttpContext context); } CredentialsStore.java000066400000000000000000000041341434266521000356550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; /** * Abstract store of authentication credentials. *

* 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 CredentialsStore extends CredentialsProvider { /** * Sets the {@link Credentials credentials} for the given authentication * scope. Any previous credentials for the given scope will be overwritten. * * @param authScope the {@link AuthScope authentication scope} * @param credentials the authentication {@link Credentials credentials} * for the given scope. * * @see #getCredentials(AuthScope, org.apache.hc.core5.http.protocol.HttpContext) */ void setCredentials(AuthScope authScope, Credentials credentials); /** * Clears all credentials. */ void clear(); } InvalidCredentialsException.java000066400000000000000000000044671434266521000400370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; /** * Authentication credentials required to respond to a authentication * challenge are invalid * * @since 4.0 */ public class InvalidCredentialsException extends AuthenticationException { private static final long serialVersionUID = -4834003835215460648L; /** * Creates a new InvalidCredentialsException with a {@code null} detail message. */ public InvalidCredentialsException() { super(); } /** * Creates a new InvalidCredentialsException with the specified message. * * @param message the exception detail message */ public InvalidCredentialsException(final String message) { super(message); } /** * Creates a new InvalidCredentialsException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public InvalidCredentialsException(final String message, final Throwable cause) { super(message, cause); } } KerberosConfig.java000066400000000000000000000115101434266521000353010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Immutable class encapsulating Kerberos configuration options. * * @since 4.6 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class KerberosConfig implements Cloneable { public enum Option { DEFAULT, ENABLE, DISABLE } public static final KerberosConfig DEFAULT = new Builder().build(); private final Option stripPort; private final Option useCanonicalHostname; private final Option requestDelegCreds; /** * Intended for CDI compatibility */ protected KerberosConfig() { this(Option.DEFAULT, Option.DEFAULT, Option.DEFAULT); } KerberosConfig( final Option stripPort, final Option useCanonicalHostname, final Option requestDelegCreds) { super(); this.stripPort = stripPort; this.useCanonicalHostname = useCanonicalHostname; this.requestDelegCreds = requestDelegCreds; } public Option getStripPort() { return stripPort; } public Option getUseCanonicalHostname() { return useCanonicalHostname; } public Option getRequestDelegCreds() { return requestDelegCreds; } @Override protected KerberosConfig clone() throws CloneNotSupportedException { return (KerberosConfig) super.clone(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("["); builder.append("stripPort=").append(stripPort); builder.append(", useCanonicalHostname=").append(useCanonicalHostname); builder.append(", requestDelegCreds=").append(requestDelegCreds); builder.append("]"); return builder.toString(); } public static KerberosConfig.Builder custom() { return new Builder(); } public static KerberosConfig.Builder copy(final KerberosConfig config) { return new Builder() .setStripPort(config.getStripPort()) .setUseCanonicalHostname(config.getUseCanonicalHostname()) .setRequestDelegCreds(config.getRequestDelegCreds()); } public static class Builder { private Option stripPort; private Option useCanonicalHostname; private Option requestDelegCreds; Builder() { super(); this.stripPort = Option.DEFAULT; this.useCanonicalHostname = Option.DEFAULT; this.requestDelegCreds = Option.DEFAULT; } public Builder setStripPort(final Option stripPort) { this.stripPort = stripPort; return this; } public Builder setStripPort(final boolean stripPort) { this.stripPort = stripPort ? Option.ENABLE : Option.DISABLE; return this; } public Builder setUseCanonicalHostname(final Option useCanonicalHostname) { this.useCanonicalHostname = useCanonicalHostname; return this; } public Builder setUseCanonicalHostname(final boolean useCanonicalHostname) { this.useCanonicalHostname = useCanonicalHostname ? Option.ENABLE : Option.DISABLE; return this; } public Builder setRequestDelegCreds(final Option requestDelegCreds) { this.requestDelegCreds = requestDelegCreds; return this; } public KerberosConfig build() { return new KerberosConfig( stripPort, useCanonicalHostname, requestDelegCreds); } } } KerberosCredentials.java000066400000000000000000000043421434266521000363360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.io.Serializable; import java.security.Principal; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.ietf.jgss.GSSCredential; /** * Kerberos specific {@link Credentials} representation based on {@link GSSCredential}. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class KerberosCredentials implements Credentials, Serializable { private static final long serialVersionUID = 487421613855550713L; /** GSSCredential */ private final GSSCredential gssCredential; /** * Constructor with GSSCredential argument * * @param gssCredential */ public KerberosCredentials(final GSSCredential gssCredential) { this.gssCredential = gssCredential; } public GSSCredential getGSSCredential() { return gssCredential; } @Override public Principal getUserPrincipal() { return null; } @Override public char[] getPassword() { return null; } } MalformedChallengeException.java000066400000000000000000000045471434266521000400030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import org.apache.hc.core5.http.ProtocolException; /** * Signals that authentication challenge is in some way invalid or * illegal in the given context * * @since 4.0 */ public class MalformedChallengeException extends ProtocolException { private static final long serialVersionUID = 814586927989932284L; /** * Creates a new MalformedChallengeException with a {@code null} detail message. */ public MalformedChallengeException() { super(); } /** * Creates a new MalformedChallengeException with the specified message. * * @param message the exception detail message */ public MalformedChallengeException(final String message) { super(message); } /** * Creates a new MalformedChallengeException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public MalformedChallengeException(final String message, final Throwable cause) { super(message, cause); } } NTCredentials.java000066400000000000000000000154731434266521000351120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.io.Serializable; import java.security.Principal; import java.util.Locale; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; /** * Microsoft Windows specific {@link Credentials} representation that includes * Windows specific attributes such as name of the domain the user belongs to. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class NTCredentials implements Credentials, Serializable { private static final long serialVersionUID = -7385699315228907265L; /** The user principal */ private final NTUserPrincipal principal; /** Password */ private final char[] password; /** The netbios hostname the authentication request is originating from. */ private final String workstation; /** The netbios domain the authentication request is against */ private final String netbiosDomain; /** * Constructor. * @param userName The user name. This should not include the domain to authenticate with. * For example: "user" is correct whereas "DOMAIN\user" is not. * @param password The password. * @param workstation The workstation the authentication request is originating from. * Essentially, the computer name for this machine. * @param domain The domain to authenticate within. */ public NTCredentials( final String userName, final char[] password, final String workstation, final String domain) { this(userName, password, convertHost(workstation), domain, convertDomain(domain)); } /** * Constructor. * @param userName The user name. This should not include the domain to authenticate with. * For example: "user" is correct whereas "DOMAIN\user" is not. * @param password The password. * @param workstation The netbios workstation name that the authentication request is originating from. * Essentially, the computer name for this machine. * @param domain The domain to authenticate within. * @param netbiosDomain The netbios version of the domain name. */ public NTCredentials( final String userName, final char[] password, final String workstation, final String domain, final String netbiosDomain) { super(); Args.notNull(userName, "User name"); this.principal = new NTUserPrincipal(domain, userName); this.password = password; if (workstation != null) { this.workstation = workstation.toUpperCase(Locale.ROOT); } else { this.workstation = null; } this.netbiosDomain = netbiosDomain; } @Override public Principal getUserPrincipal() { return this.principal; } public String getUserName() { return this.principal.getUsername(); } @Override public char[] getPassword() { return this.password; } /** * Retrieves the name to authenticate with. * * @return String the domain these credentials are intended to authenticate with. */ public String getDomain() { return this.principal.getDomain(); } /** * Retrieves the netbios domain to authenticate with. * @return String the netbios domain name. */ public String getNetbiosDomain() { return this.netbiosDomain; } /** * Retrieves the netbios workstation name of the computer originating the request. * * @return String the netbios workstation the user is logged into. */ public String getWorkstation() { return this.workstation; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.principal); hash = LangUtils.hashCode(hash, this.workstation); hash = LangUtils.hashCode(hash, this.netbiosDomain); return hash; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o instanceof NTCredentials) { final NTCredentials that = (NTCredentials) o; return Objects.equals(this.principal, that.principal) && Objects.equals(this.workstation, that.workstation) && Objects.equals(this.netbiosDomain, that.netbiosDomain); } return false; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[principal: "); buffer.append(this.principal); buffer.append("][workstation: "); buffer.append(this.workstation); buffer.append("][netbiosDomain: "); buffer.append(this.netbiosDomain); buffer.append("]"); return buffer.toString(); } /** Strip dot suffix from a name */ private static String stripDotSuffix(final String value) { if (value == null) { return null; } final int index = value.indexOf('.'); if (index != -1) { return value.substring(0, index); } return value; } /** Convert host to standard form */ private static String convertHost(final String host) { return stripDotSuffix(host); } /** Convert domain to standard form */ private static String convertDomain(final String domain) { final String returnString = stripDotSuffix(domain); return returnString == null ? returnString : returnString.toUpperCase(Locale.ROOT); } } NTUserPrincipal.java000066400000000000000000000066741434266521000354400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.io.Serializable; import java.security.Principal; import java.util.Locale; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; /** * Microsoft Windows specific user principal implementation. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class NTUserPrincipal implements Principal, Serializable { private static final long serialVersionUID = -6870169797924406894L; private final String username; private final String domain; private final String ntname; public NTUserPrincipal( final String domain, final String username) { super(); Args.notNull(username, "User name"); this.username = username; if (domain != null) { this.domain = domain.toUpperCase(Locale.ROOT); } else { this.domain = null; } if (this.domain != null && !this.domain.isEmpty()) { final StringBuilder buffer = new StringBuilder(); buffer.append(this.domain); buffer.append('\\'); buffer.append(this.username); this.ntname = buffer.toString(); } else { this.ntname = this.username; } } @Override public String getName() { return this.ntname; } public String getDomain() { return this.domain; } public String getUsername() { return this.username; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.username); hash = LangUtils.hashCode(hash, this.domain); return hash; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o instanceof NTUserPrincipal) { final NTUserPrincipal that = (NTUserPrincipal) o; return Objects.equals(this.username, that.username) && Objects.equals(this.domain, that.domain); } return false; } @Override public String toString() { return this.ntname; } } StandardAuthScheme.java000066400000000000000000000046421434266521000361160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; /** * Authentication schemes by their names supported by the HttpClient. * * @since 4.3 */ public final class StandardAuthScheme { private StandardAuthScheme() { // no instances } /** * Basic authentication scheme as defined in RFC 2617 (considered inherently * insecure without transport encryption, but most widely supported). */ public static final String BASIC = "Basic"; /** * Digest authentication scheme as defined in RFC 2617. */ public static final String DIGEST = "Digest"; /** * The NTLM authentication scheme is a proprietary Microsoft Windows * authentication protocol as defined in [MS-NLMP]. */ public static final String NTLM = "NTLM"; /** * SPNEGO authentication scheme as defined in RFC 4559 and RFC 4178 * (considered to be the most secure among currently supported * authentication schemes if Kerberos is selected). */ public static final String SPNEGO = "Negotiate"; /** * Kerberos authentication scheme as defined in RFC 4120 * (considered to be the most secure among currently supported * authentication schemes). */ public static final String KERBEROS = "Kerberos"; } UsernamePasswordCredentials.java000066400000000000000000000060031434266521000400600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.io.Serializable; import java.security.Principal; import java.util.Objects; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Simple {@link Credentials} representation based on a user name / password * pair. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class UsernamePasswordCredentials implements Credentials, Serializable { private static final long serialVersionUID = 243343858802739403L; private final BasicUserPrincipal principal; private final char[] password; /** * The constructor with the username and password arguments. * * @param userName the user name * @param password the password */ public UsernamePasswordCredentials(final String userName, final char[] password) { super(); Args.notNull(userName, "Username"); this.principal = new BasicUserPrincipal(userName); this.password = password; } @Override public Principal getUserPrincipal() { return this.principal; } public String getUserName() { return this.principal.getName(); } @Override public char[] getPassword() { return password; } @Override public int hashCode() { return this.principal.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o instanceof UsernamePasswordCredentials) { final UsernamePasswordCredentials that = (UsernamePasswordCredentials) o; return Objects.equals(this.principal, that.principal); } return false; } @Override public String toString() { return this.principal.toString(); } } package-info.java000066400000000000000000000023661434266521000347340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client HTTP authentication APIs. */ package org.apache.hc.client5.http.auth; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/000077500000000000000000000000001434266521000322755ustar00rootroot00000000000000BackoffManager.java000066400000000000000000000035051434266521000357120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic; import org.apache.hc.client5.http.HttpRoute; /** * Represents a controller that dynamically adjusts the size * of an available connection pool based on feedback from * using the connections. * * @since 4.2 */ public interface BackoffManager { /** * Called when we have decided that the result of * using a connection should be interpreted as a * backoff signal. */ void backOff(HttpRoute route); /** * Called when we have determined that the result of * using a connection has succeeded and that we may * probe for more connections. */ void probe(HttpRoute route); } ConnectionBackoffStrategy.java000066400000000000000000000046721434266521000401700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic; import org.apache.hc.core5.http.HttpResponse; /** * When managing a dynamic number of connections for a given route, this * strategy assesses whether a given request execution outcome should * result in a backoff signal or not, based on either examining the * {@code Throwable} that resulted or by examining the resulting * response (e.g. for its status code). * * @since 4.2 * */ public interface ConnectionBackoffStrategy { /** * Determines whether seeing the given {@code Throwable} as * a result of request execution should result in a backoff * signal. * @param t the {@code Throwable} that happened * @return {@code true} if a backoff signal should be * given */ boolean shouldBackoff(Throwable t); /** * Determines whether receiving the given {@link HttpResponse} as * a result of request execution should result in a backoff * signal. Implementations MUST restrict themselves to examining * the response header and MUST NOT consume any of the response * body, if any. * @param response the {@code HttpResponse} that was received * @return {@code true} if a backoff signal should be * given */ boolean shouldBackoff(HttpResponse response); } ExecChain.java000066400000000000000000000054241434266521000347150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic; import java.io.IOException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.util.Args; /** * Represents a single element in the client side classic request execution chain. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ExecChain { final class Scope { public final String exchangeId; public final HttpRoute route; public final ClassicHttpRequest originalRequest; public final ExecRuntime execRuntime; public final HttpClientContext clientContext; public Scope(final String exchangeId, final HttpRoute route, final ClassicHttpRequest originalRequest, final ExecRuntime execRuntime, final HttpClientContext clientContext) { this.exchangeId = Args.notNull(exchangeId, "Exchange id"); this.route = Args.notNull(route, "Route"); this.originalRequest = Args.notNull(originalRequest, "Original request"); this.execRuntime = Args.notNull(execRuntime, "Exec runtime"); this.clientContext = clientContext != null ? clientContext : HttpClientContext.create(); } } ClassicHttpResponse proceed( ClassicHttpRequest request, Scope scope) throws IOException, HttpException; } ExecChainHandler.java000066400000000000000000000053721434266521000362150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; /** * Abstract request execution handler in a classic client side request execution chain. * Handlers can either be a decorator around another element that implements a cross * cutting aspect or a self-contained executor capable of producing a response * for the given request. *

* Important: please note it is required for decorators that implement post execution aspects * or response post-processing of any sort to release resources associated with the response * by calling {@link ClassicHttpResponse#close()} methods in case of an I/O, protocol or * runtime exception, or in case the response is not propagated to the caller. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ExecChainHandler { /** * Executes the actual HTTP request. The handler can choose to return * a response message or delegate request execution to the next element * in the execution chain. * * @param request the actual request. * @param scope the execution scope . * @param chain the next element in the request execution chain. */ ClassicHttpResponse execute( ClassicHttpRequest request, ExecChain.Scope scope, ExecChain chain) throws IOException, HttpException; } ExecRuntime.java000066400000000000000000000127521434266521000353200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic; import java.io.IOException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.util.TimeValue; /** * Execution runtime that provides access to the underlying connection endpoint and helps * manager its life cycle. *

* This interface is considered internal and generally ought not be used or accessed * by custom request exec handlers. * * @since 5.0 */ @Internal public interface ExecRuntime { /** * Determines of the request execution has been aborted. * * @return {@code true} if the request execution has been acquired, * {@code false} otherwise. */ boolean isExecutionAborted(); /** * Determines of a connection endpoint has been acquired. * * @return {@code true} if an endpoint has been acquired, {@code false} otherwise. */ boolean isEndpointAcquired(); /** * Acquires a connection endpoint. Endpoints can leased from a pool * or unconnected new endpoint can be created. * * @param id unique operation ID or {@code null}. * @param route the connection route. * @param state the expected connection state. May be {@code null} if connection * can be state-less or its state is irrelevant. * @param context the execution context. */ void acquireEndpoint( String id, HttpRoute route, Object state, HttpClientContext context) throws IOException; /** * Releases the acquired endpoint potentially making it available for re-use. */ void releaseEndpoint(); /** * Shuts down and discards the acquired endpoint. */ void discardEndpoint(); /** * Determines of there the endpoint is connected to the initial hop (connection * target in case of a direct route or to the first proxy hop in case of a route * via a proxy or multiple proxies). * * @return {@code true} if the endpoint is connected, {@code false} otherwise. */ boolean isEndpointConnected(); /** * Disconnects the local endpoint from the initial hop in the connection route. */ void disconnectEndpoint() throws IOException; /** * Connect the local endpoint to the initial hop (connection target in case * of a direct route or to the first proxy hop in case of a route via a proxy * or multiple proxies). * * @param context the execution context. */ void connectEndpoint(HttpClientContext context) throws IOException; /** * Upgrades transport security of the active connection by using the TLS security protocol. * * @param context the execution context. */ void upgradeTls(HttpClientContext context) throws IOException; /** * Executes HTTP request using the given context. * * @param id unique operation ID or {@code null}. * @param request the request message. * @param context the execution context. */ ClassicHttpResponse execute( String id, ClassicHttpRequest request, HttpClientContext context) throws IOException, HttpException; /** * Determines of the connection is considered re-usable. * * @return {@code true} if the connection is re-usable, {@code false} otherwise. */ boolean isConnectionReusable(); /** * Marks the connection as potentially re-usable for the given period of time * and also marks it as stateful if the state representation is given. * @param state the connection state representation or {@code null} if stateless. * @param validityTime the period of time this connection is valid for. */ void markConnectionReusable(Object state, TimeValue validityTime); /** * Marks the connection as non re-usable. */ void markConnectionNonReusable(); /** * Forks this runtime for parallel execution. * * @return another runtime with the same configuration. */ ExecRuntime fork(CancellableDependency cancellableAware); } HttpClient.java000066400000000000000000000332161434266521000351440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic; import java.io.Closeable; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.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 HttpClient { /** * Executes HTTP request using the default context. * * @param request the request to execute * * @return the response to the request. This is always a final response, * never an intermediate response with an 1xx status code. * Whether redirects or authentication challenges will be returned * or handled automatically depends on the implementation and * configuration of this client. * @throws IOException in case of a problem or the connection was aborted * * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(ClassicHttpRequest, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(ClassicHttpRequest, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated HttpResponse execute(ClassicHttpRequest request) throws IOException; /** * Executes HTTP request using the given context. * * @param request the request to execute * @param context the context to use for the execution, or * {@code null} to use the default context * * @return the response to the request. This is always a final response, * never an intermediate response with an 1xx status code. * Whether redirects or authentication challenges will be returned * or handled automatically depends on the implementation and * configuration of this client. * @throws IOException in case of a problem or the connection was aborted * * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(ClassicHttpRequest, HttpContext, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(ClassicHttpRequest, HttpContext, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated HttpResponse execute(ClassicHttpRequest request, HttpContext context) throws IOException; /** * Executes HTTP request using the default context. * * @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 * * @return the response to the request. This is always a final response, * never an intermediate response with an 1xx status code. * Whether redirects or authentication challenges will be returned * or handled automatically depends on the implementation and * configuration of this client. * @throws IOException in case of a problem or the connection was aborted * * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(HttpHost, ClassicHttpRequest, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(HttpHost, ClassicHttpRequest, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated ClassicHttpResponse execute(HttpHost target, ClassicHttpRequest request) throws IOException; /** * Executes HTTP request using the given context. * * @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 * * @return the response to the request. This is always a final response, * never an intermediate response with an 1xx status code. * Whether redirects or authentication challenges will be returned * or handled automatically depends on the implementation and * configuration of this client. * @throws IOException in case of a problem or the connection was aborted * * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(HttpHost, ClassicHttpRequest, HttpContext, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(HttpHost, ClassicHttpRequest, HttpContext, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated HttpResponse execute(HttpHost target, ClassicHttpRequest request, HttpContext context) throws IOException; /** * Executes the request and opens the response stream using the given context. * * @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 * * @return the response to the request. This is always a final response, * never an intermediate response with an 1xx status code. * Whether redirects or authentication challenges will be returned * or handled automatically depends on the implementation and * configuration of this client. * The response returned by this method must be closed with * {@link Closeable#close()} in order ensure deallocation * of system resources. * @throws IOException in case of a problem or the connection was aborted * * @since 5.2 */ @SuppressWarnings("deprecation") default ClassicHttpResponse executeOpen(HttpHost target, ClassicHttpRequest request, HttpContext context) throws IOException { return (ClassicHttpResponse) execute(target, request, context); } /** * Executes HTTP request using the default context and processes the * response using the given response handler. *

* Implementing classes are required to ensure that the content entity * associated with the response is fully consumed and the underlying * connection is released back to the connection manager automatically * in all cases relieving individual {@link HttpClientResponseHandler}s from * having to manage resource deallocation internally. *

* * @param request the request to execute * @param responseHandler the response handler * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted */ T execute(ClassicHttpRequest request, HttpClientResponseHandler responseHandler) throws IOException; /** * Executes HTTP request using the given context and processes the * response using the given response handler. *

* Implementing classes are required to ensure that the content entity * associated with the response is fully consumed and the underlying * connection is released back to the connection manager automatically * in all cases relieving individual {@link HttpClientResponseHandler}s from * having to manage resource deallocation internally. *

* * @param request the request to execute * @param context the context to use for the execution, or * {@code null} to use the default context * @param responseHandler the response handler * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted */ T execute( ClassicHttpRequest request, HttpContext context, HttpClientResponseHandler responseHandler) throws IOException; /** * Executes HTTP request to the target using the default context and * processes the response using the given response handler. *

* Implementing classes are required to ensure that the content entity * associated with the response is fully consumed and the underlying * connection is released back to the connection manager automatically * in all cases relieving individual {@link HttpClientResponseHandler}s from * having to manage resource deallocation internally. *

* * @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 responseHandler the response handler * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted */ T execute( HttpHost target, ClassicHttpRequest request, HttpClientResponseHandler responseHandler) throws IOException; /** * Executes HTTP request to the target using the given context and * processes the response using the given response handler. *

* Implementing classes are required to ensure that the content entity * associated with the response is fully consumed and the underlying * connection is released back to the connection manager automatically * in all cases relieving individual {@link HttpClientResponseHandler}s from * having to manage resource deallocation internally. *

* * @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 responseHandler the response handler * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted */ T execute( HttpHost target, ClassicHttpRequest request, HttpContext context, HttpClientResponseHandler responseHandler) throws IOException; } methods/000077500000000000000000000000001434266521000336615ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classicClassicHttpRequests.java000066400000000000000000000127021434266521000405030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.util.Args; /** * Common HTTP methods using {@link HttpUriRequest} as a HTTP request message representation. *

* Each static method creates a request object of the exact subclass of {@link HttpUriRequest} * with a non-null URI. * * @since 5.0 * * @deprecated Use {@link org.apache.hc.core5.http.io.support.ClassicRequestBuilder} */ @Deprecated public final class ClassicHttpRequests { /** * Creates a new HttpUriRequest for the given {@code Method} and {@code String} URI. * * @param method A method. * @param uri a URI. * @return a new HttpUriRequest. */ public static HttpUriRequest create(final Method method, final String uri) { return create(method, URI.create(uri)); } /** * Creates a new HttpUriRequest for the given {@code Method} and {@code URI}. * * @param method A method. * @param uri a URI. * @return a new HttpUriRequest. */ public static HttpUriRequest create(final Method method, final URI uri) { switch (Args.notNull(method, "method")) { case DELETE: return delete(uri); case GET: return get(uri); case HEAD: return head(uri); case OPTIONS: return options(uri); case PATCH: return patch(uri); case POST: return post(uri); case PUT: return put(uri); case TRACE: return trace(uri); default: throw new IllegalArgumentException(method.toString()); } } /** * Creates a new HttpUriRequest for the given {@code method} and {@code String} URI. * * @param method A method supported by this class. * @param uri a non-null request string URI. * @throws IllegalArgumentException if the method is not supported. * @throws IllegalArgumentException if the string uri is null. * @return A new HttpUriRequest. */ public static HttpUriRequest create(final String method, final String uri) { return create(Method.normalizedValueOf(method), uri); } /** * Creates a new HttpUriRequest for the given {@code method} and {@code URI}. * * @param method A method supported by this class. * @param uri a non-null request URI. * @throws IllegalArgumentException if the method is not supported. * @throws IllegalArgumentException if the uri is null. * @return A new HttpUriRequest. */ public static HttpUriRequest create(final String method, final URI uri) { return create(Method.normalizedValueOf(method), uri); } public static HttpUriRequest delete(final String uri) { return delete(URI.create(uri)); } public static HttpUriRequest delete(final URI uri) { return new HttpDelete(uri); } public static HttpUriRequest get(final String uri) { return get(URI.create(uri)); } public static HttpUriRequest get(final URI uri) { return new HttpGet(uri); } public static HttpUriRequest head(final String uri) { return head(URI.create(uri)); } public static HttpUriRequest head(final URI uri) { return new HttpHead(uri); } public static HttpUriRequest options(final String uri) { return options(URI.create(uri)); } public static HttpUriRequest options(final URI uri) { return new HttpOptions(uri); } public static HttpUriRequest patch(final String uri) { return patch(URI.create(uri)); } public static HttpUriRequest patch(final URI uri) { return new HttpPatch(uri); } public static HttpUriRequest post(final String uri) { return post(URI.create(uri)); } public static HttpUriRequest post(final URI uri) { return new HttpPost(uri); } public static HttpUriRequest put(final String uri) { return put(URI.create(uri)); } public static HttpUriRequest put(final URI uri) { return new HttpPut(uri); } public static HttpUriRequest trace(final String uri) { return trace(URI.create(uri)); } public static HttpUriRequest trace(final URI uri) { return new HttpTrace(uri); } } HttpDelete.java000066400000000000000000000037331434266521000365740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; /** * HTTP DELETE method. * * @since 4.0 */ public class HttpDelete extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "DELETE"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpDelete(final URI uri) { super(METHOD_NAME, uri); } /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpDelete(final String uri) { this(URI.create(uri)); } } HttpGet.java000066400000000000000000000037131434266521000361070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; /** * HTTP GET method. * * @since 4.0 */ public class HttpGet extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "GET"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpGet(final URI uri) { super(METHOD_NAME, uri); } /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpGet(final String uri) { this(URI.create(uri)); } } HttpHead.java000066400000000000000000000036131434266521000362300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; /** * HTTP HEAD method. * * @since 4.0 */ public class HttpHead extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "HEAD"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpHead(final URI uri) { super(METHOD_NAME, uri); } /** * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpHead(final String uri) { this(URI.create(uri)); } } HttpOptions.java000066400000000000000000000053451434266521000370260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.util.Args; /** * HTTP OPTIONS method. * * @since 4.0 */ public class HttpOptions extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "OPTIONS"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpOptions(final URI uri) { super(METHOD_NAME, uri); } /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpOptions(final String uri) { this(URI.create(uri)); } @Override public String getMethod() { return METHOD_NAME; } public Set getAllowedMethods(final HttpResponse response) { Args.notNull(response, "HTTP response"); final Iterator it = MessageSupport.iterate(response, "Allow"); final Set methods = new HashSet<>(); while (it.hasNext()) { final HeaderElement element = it.next(); methods.add(element.getName()); } return methods; } } HttpPatch.java000066400000000000000000000037251434266521000364320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; /** * HTTP PATCH method. * * @since 4.2 */ public class HttpPatch extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "PATCH"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpPatch(final URI uri) { super(METHOD_NAME, uri); } /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpPatch(final String uri) { this(URI.create(uri)); } } HttpPost.java000066400000000000000000000037201434266521000363130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; /** * HTTP POST method. * * @since 4.0 */ public class HttpPost extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "POST"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpPost(final URI uri) { super(METHOD_NAME, uri); } /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpPost(final String uri) { this(URI.create(uri)); } } HttpPut.java000066400000000000000000000037121434266521000361370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; /** * HTTP PUT method. * * @since 4.0 */ public class HttpPut extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "PUT"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpPut(final URI uri) { super(METHOD_NAME, uri); } /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpPut(final String uri) { this(URI.create(uri)); } } HttpTrace.java000066400000000000000000000042531434266521000364260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; import org.apache.hc.core5.http.HttpEntity; /** * HTTP TRACE method. * * @since 4.0 */ public class HttpTrace extends HttpUriRequestBase { private static final long serialVersionUID = 1L; public final static String METHOD_NAME = "TRACE"; /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is null. */ public HttpTrace(final URI uri) { super(METHOD_NAME, uri); } /** * Creates a new instance initialized with the given URI. * * @param uri a non-null request URI. * @throws IllegalArgumentException if the uri is invalid. */ public HttpTrace(final String uri) { this(URI.create(uri)); } @Override public void setEntity(final HttpEntity entity) { throw new IllegalStateException(METHOD_NAME + " requests may not include an entity."); } } HttpUriRequest.java000066400000000000000000000040231434266521000374730ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.core5.http.ClassicHttpRequest; /** * Extended version of the {@link ClassicHttpRequest} interface that provides * convenience methods to access request properties such as request URI * and method type. * * @since 4.0 */ public interface HttpUriRequest extends ClassicHttpRequest, Configurable { /** * Aborts execution of the request. * * @throws UnsupportedOperationException if the abort operation * is not supported / cannot be implemented. */ void abort() throws UnsupportedOperationException; /** * Tests if the request execution has been aborted. * * @return {@code true} if the request execution has been aborted, * {@code false} otherwise. */ boolean isAborted(); } HttpUriRequestBase.java000066400000000000000000000077271434266521000403040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.net.URI; import java.util.concurrent.atomic.AtomicMarkableReference; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; public class HttpUriRequestBase extends BasicClassicHttpRequest implements HttpUriRequest, CancellableDependency { private static final long serialVersionUID = 1L; private final AtomicMarkableReference cancellableRef; private RequestConfig requestConfig; public HttpUriRequestBase(final String method, final URI requestUri) { super(method, requestUri); this.cancellableRef = new AtomicMarkableReference<>(null, false); } @Override public boolean cancel() { while (!cancellableRef.isMarked()) { final Cancellable actualCancellable = cancellableRef.getReference(); if (cancellableRef.compareAndSet(actualCancellable, actualCancellable, false, true)) { if (actualCancellable != null) { actualCancellable.cancel(); } return true; } } return false; } @Override public boolean isCancelled() { return cancellableRef.isMarked(); } /** * @since 4.2 */ @Override public void setDependency(final Cancellable cancellable) { final Cancellable actualCancellable = cancellableRef.getReference(); if (!cancellableRef.compareAndSet(actualCancellable, cancellable, false, false)) { cancellable.cancel(); } } /** * Resets internal state of the request making it reusable. * * @since 4.2 */ public void reset() { for (;;) { final boolean marked = cancellableRef.isMarked(); final Cancellable actualCancellable = cancellableRef.getReference(); if (actualCancellable != null) { actualCancellable.cancel(); } if (cancellableRef.compareAndSet(actualCancellable, null, marked, false)) { break; } } } @Override public void abort() throws UnsupportedOperationException { cancel(); } @Override public boolean isAborted() { return isCancelled(); } public void setConfig(final RequestConfig requestConfig) { this.requestConfig = requestConfig; } @Override public RequestConfig getConfig() { return requestConfig; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(getMethod()).append(" ").append(getRequestUri()); return sb.toString(); } } package-info.java000066400000000000000000000024441434266521000370540ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Common HTTP methods and message handlers for the classic I/O model. */ package org.apache.hc.client5.http.classic.methods; package-info.java000066400000000000000000000024241434266521000354070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP client APIs based on the classic (blocking) I/O model. */ package org.apache.hc.client5.http.classic; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/config/000077500000000000000000000000001434266521000321215ustar00rootroot00000000000000Configurable.java000066400000000000000000000026241434266521000353110ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/config/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.config; /** * Configuration interface for HTTP requests. * * @since 4.3 */ public interface Configurable { /** * Returns actual request configuration. */ RequestConfig getConfig(); } ConnectionConfig.java000066400000000000000000000170061434266521000361360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/config/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.config; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Immutable class encapsulating connection initialization and management settings. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class ConnectionConfig implements Cloneable { private static final Timeout DEFAULT_CONNECT_TIMEOUT = Timeout.ofMinutes(3); public static final ConnectionConfig DEFAULT = new Builder().build(); private final Timeout connectTimeout; private final Timeout socketTimeout; private final TimeValue validateAfterInactivity; private final TimeValue timeToLive; /** * Intended for CDI compatibility */ protected ConnectionConfig() { this(DEFAULT_CONNECT_TIMEOUT, null, null, null); } ConnectionConfig( final Timeout connectTimeout, final Timeout socketTimeout, final TimeValue validateAfterInactivity, final TimeValue timeToLive) { super(); this.connectTimeout = connectTimeout; this.socketTimeout = socketTimeout; this.validateAfterInactivity = validateAfterInactivity; this.timeToLive = timeToLive; } /** * @see Builder#setSocketTimeout(Timeout) */ public Timeout getSocketTimeout() { return socketTimeout; } /** * @see Builder#setConnectTimeout(Timeout) */ public Timeout getConnectTimeout() { return connectTimeout; } /** * @see Builder#setValidateAfterInactivity(TimeValue) */ public TimeValue getValidateAfterInactivity() { return validateAfterInactivity; } /** * @see Builder#setTimeToLive(TimeValue) (TimeValue) */ public TimeValue getTimeToLive() { return timeToLive; } @Override protected ConnectionConfig clone() throws CloneNotSupportedException { return (ConnectionConfig) super.clone(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("["); builder.append("connectTimeout=").append(connectTimeout); builder.append(", socketTimeout=").append(socketTimeout); builder.append(", validateAfterInactivity=").append(validateAfterInactivity); builder.append(", timeToLive=").append(timeToLive); builder.append("]"); return builder.toString(); } public static ConnectionConfig.Builder custom() { return new Builder(); } public static ConnectionConfig.Builder copy(final ConnectionConfig config) { return new Builder() .setConnectTimeout(config.getConnectTimeout()) .setSocketTimeout(config.getSocketTimeout()) .setValidateAfterInactivity(config.getValidateAfterInactivity()) .setTimeToLive(config.getTimeToLive()); } public static class Builder { private Timeout socketTimeout; private Timeout connectTimeout; private TimeValue validateAfterInactivity; private TimeValue timeToLive; Builder() { super(); this.connectTimeout = DEFAULT_CONNECT_TIMEOUT; } /** * @see #setSocketTimeout(Timeout) */ public Builder setSocketTimeout(final int soTimeout, final TimeUnit timeUnit) { this.socketTimeout = Timeout.of(soTimeout, timeUnit); return this; } /** * Determines the default socket timeout value for I/O operations. *

* Default: {@code null} (undefined) *

* * @return the default socket timeout value for I/O operations. */ public Builder setSocketTimeout(final Timeout soTimeout) { this.socketTimeout = soTimeout; return this; } /** * Determines the timeout until a new connection is fully established. *

* A timeout value of zero is interpreted as an infinite timeout. *

*

* Default: 3 minutes *

*/ public Builder setConnectTimeout(final Timeout connectTimeout) { this.connectTimeout = connectTimeout; return this; } /** * @see #setConnectTimeout(Timeout) */ public Builder setConnectTimeout(final long connectTimeout, final TimeUnit timeUnit) { this.connectTimeout = Timeout.of(connectTimeout, timeUnit); return this; } /** * Defines period of inactivity after which persistent connections must * be re-validated prior to being leased to the consumer. Negative values passed * to this method disable connection validation. *

* Default: {@code null} (undefined) *

*/ public Builder setValidateAfterInactivity(final TimeValue validateAfterInactivity) { this.validateAfterInactivity = validateAfterInactivity; return this; } /** * @see #setValidateAfterInactivity(TimeValue) */ public Builder setValidateAfterInactivity(final long validateAfterInactivity, final TimeUnit timeUnit) { this.validateAfterInactivity = TimeValue.of(validateAfterInactivity, timeUnit); return this; } /** * Defines the total span of time connections can be kept alive or execute requests. *

* Default: {@code null} (undefined) *

*/ public Builder setTimeToLive(final TimeValue timeToLive) { this.timeToLive = timeToLive; return this; } /** * @see #setTimeToLive(TimeValue) */ public Builder setTimeToLive(final long timeToLive, final TimeUnit timeUnit) { this.timeToLive = TimeValue.of(timeToLive, timeUnit); return this; } public ConnectionConfig build() { return new ConnectionConfig( connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT, socketTimeout, validateAfterInactivity, timeToLive); } } } RequestConfig.java000066400000000000000000000526201434266521000354700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/config/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.config; import java.util.Collection; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Immutable class encapsulating request configuration items. */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class RequestConfig implements Cloneable { private static final Timeout DEFAULT_CONNECTION_REQUEST_TIMEOUT = Timeout.ofMinutes(3); private static final TimeValue DEFAULT_CONN_KEEP_ALIVE = TimeValue.ofMinutes(3); public static final RequestConfig DEFAULT = new Builder().build(); private final boolean expectContinueEnabled; private final HttpHost proxy; private final String cookieSpec; private final boolean redirectsEnabled; private final boolean circularRedirectsAllowed; private final int maxRedirects; private final boolean authenticationEnabled; private final Collection targetPreferredAuthSchemes; private final Collection proxyPreferredAuthSchemes; private final Timeout connectionRequestTimeout; private final Timeout connectTimeout; private final Timeout responseTimeout; private final TimeValue connectionKeepAlive; private final boolean contentCompressionEnabled; private final boolean hardCancellationEnabled; /** * Intended for CDI compatibility */ protected RequestConfig() { this(false, null, null, false, false, 0, false, null, null, DEFAULT_CONNECTION_REQUEST_TIMEOUT, null, null, DEFAULT_CONN_KEEP_ALIVE, false, false); } RequestConfig( final boolean expectContinueEnabled, final HttpHost proxy, final String cookieSpec, final boolean redirectsEnabled, final boolean circularRedirectsAllowed, final int maxRedirects, final boolean authenticationEnabled, final Collection targetPreferredAuthSchemes, final Collection proxyPreferredAuthSchemes, final Timeout connectionRequestTimeout, final Timeout connectTimeout, final Timeout responseTimeout, final TimeValue connectionKeepAlive, final boolean contentCompressionEnabled, final boolean hardCancellationEnabled) { super(); this.expectContinueEnabled = expectContinueEnabled; this.proxy = proxy; this.cookieSpec = cookieSpec; this.redirectsEnabled = redirectsEnabled; this.circularRedirectsAllowed = circularRedirectsAllowed; this.maxRedirects = maxRedirects; this.authenticationEnabled = authenticationEnabled; this.targetPreferredAuthSchemes = targetPreferredAuthSchemes; this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes; this.connectionRequestTimeout = connectionRequestTimeout; this.connectTimeout = connectTimeout; this.responseTimeout = responseTimeout; this.connectionKeepAlive = connectionKeepAlive; this.contentCompressionEnabled = contentCompressionEnabled; this.hardCancellationEnabled = hardCancellationEnabled; } /** * @see Builder#setExpectContinueEnabled(boolean) */ public boolean isExpectContinueEnabled() { return expectContinueEnabled; } /** * @see Builder#setProxy(HttpHost) * * @deprecated Use {@link org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner} * or a custom {@link org.apache.hc.client5.http.routing.HttpRoutePlanner}. */ @Deprecated public HttpHost getProxy() { return proxy; } /** * @see Builder#setCookieSpec(String) */ public String getCookieSpec() { return cookieSpec; } /** * @see Builder#setRedirectsEnabled(boolean) */ public boolean isRedirectsEnabled() { return redirectsEnabled; } /** * @see Builder#setCircularRedirectsAllowed(boolean) */ public boolean isCircularRedirectsAllowed() { return circularRedirectsAllowed; } /** * @see Builder#setMaxRedirects(int) */ public int getMaxRedirects() { return maxRedirects; } /** * @see Builder#setAuthenticationEnabled(boolean) */ public boolean isAuthenticationEnabled() { return authenticationEnabled; } /** * @see Builder#setTargetPreferredAuthSchemes(Collection) */ public Collection getTargetPreferredAuthSchemes() { return targetPreferredAuthSchemes; } /** * @see Builder#setProxyPreferredAuthSchemes(Collection) */ public Collection getProxyPreferredAuthSchemes() { return proxyPreferredAuthSchemes; } /** * @see Builder#setConnectionRequestTimeout(Timeout) */ public Timeout getConnectionRequestTimeout() { return connectionRequestTimeout; } /** * @see Builder#setConnectTimeout(Timeout) * * @deprecated Use {@link ConnectionConfig#getConnectTimeout()}. */ @Deprecated public Timeout getConnectTimeout() { return connectTimeout; } /** * @see Builder#setResponseTimeout(Timeout) */ public Timeout getResponseTimeout() { return responseTimeout; } /** * @see Builder#setConnectionKeepAlive(TimeValue) */ public TimeValue getConnectionKeepAlive() { return connectionKeepAlive; } /** * @see Builder#setContentCompressionEnabled(boolean) */ public boolean isContentCompressionEnabled() { return contentCompressionEnabled; } /** * @see Builder#setHardCancellationEnabled(boolean) */ public boolean isHardCancellationEnabled() { return hardCancellationEnabled; } @Override protected RequestConfig clone() throws CloneNotSupportedException { return (RequestConfig) super.clone(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("["); builder.append("expectContinueEnabled=").append(expectContinueEnabled); builder.append(", proxy=").append(proxy); builder.append(", cookieSpec=").append(cookieSpec); builder.append(", redirectsEnabled=").append(redirectsEnabled); builder.append(", maxRedirects=").append(maxRedirects); builder.append(", circularRedirectsAllowed=").append(circularRedirectsAllowed); builder.append(", authenticationEnabled=").append(authenticationEnabled); builder.append(", targetPreferredAuthSchemes=").append(targetPreferredAuthSchemes); builder.append(", proxyPreferredAuthSchemes=").append(proxyPreferredAuthSchemes); builder.append(", connectionRequestTimeout=").append(connectionRequestTimeout); builder.append(", connectTimeout=").append(connectTimeout); builder.append(", responseTimeout=").append(responseTimeout); builder.append(", connectionKeepAlive=").append(connectionKeepAlive); builder.append(", contentCompressionEnabled=").append(contentCompressionEnabled); builder.append(", hardCancellationEnabled=").append(hardCancellationEnabled); builder.append("]"); return builder.toString(); } public static RequestConfig.Builder custom() { return new Builder(); } @SuppressWarnings("deprecation") public static RequestConfig.Builder copy(final RequestConfig config) { return new Builder() .setExpectContinueEnabled(config.isExpectContinueEnabled()) .setProxy(config.getProxy()) .setCookieSpec(config.getCookieSpec()) .setRedirectsEnabled(config.isRedirectsEnabled()) .setCircularRedirectsAllowed(config.isCircularRedirectsAllowed()) .setMaxRedirects(config.getMaxRedirects()) .setAuthenticationEnabled(config.isAuthenticationEnabled()) .setTargetPreferredAuthSchemes(config.getTargetPreferredAuthSchemes()) .setProxyPreferredAuthSchemes(config.getProxyPreferredAuthSchemes()) .setConnectionRequestTimeout(config.getConnectionRequestTimeout()) .setConnectTimeout(config.getConnectTimeout()) .setResponseTimeout(config.getResponseTimeout()) .setConnectionKeepAlive(config.getConnectionKeepAlive()) .setContentCompressionEnabled(config.isContentCompressionEnabled()) .setHardCancellationEnabled(config.isHardCancellationEnabled()); } public static class Builder { private boolean expectContinueEnabled; private HttpHost proxy; private String cookieSpec; private boolean redirectsEnabled; private boolean circularRedirectsAllowed; private int maxRedirects; private boolean authenticationEnabled; private Collection targetPreferredAuthSchemes; private Collection proxyPreferredAuthSchemes; private Timeout connectionRequestTimeout; private Timeout connectTimeout; private Timeout responseTimeout; private TimeValue connectionKeepAlive; private boolean contentCompressionEnabled; private boolean hardCancellationEnabled; Builder() { super(); this.redirectsEnabled = true; this.maxRedirects = 50; this.authenticationEnabled = true; this.connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT; this.contentCompressionEnabled = true; this.hardCancellationEnabled = true; } /** * Determines whether the 'Expect: 100-Continue' handshake is enabled * for entity enclosing methods. The purpose of the 'Expect: 100-Continue' * handshake is to allow a client that is sending a request message with * a request body to determine if the origin server is willing to * accept the request (based on the request headers) before the client * sends the request body. *

* The use of the 'Expect: 100-continue' handshake can result in * a noticeable performance improvement for entity enclosing requests * (such as POST and PUT) that require the target server's * authentication. *

*

* 'Expect: 100-continue' handshake should be used with caution, as it * may cause problems with HTTP servers and proxies that do not support * HTTP/1.1 protocol. *

*

* Default: {@code false} *

*/ public Builder setExpectContinueEnabled(final boolean expectContinueEnabled) { this.expectContinueEnabled = expectContinueEnabled; return this; } /** * Returns HTTP proxy to be used for request execution. *

* Default: {@code null} *

* * @deprecated Use {@link org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner} * or a custom {@link org.apache.hc.client5.http.routing.HttpRoutePlanner}. */ @Deprecated public Builder setProxy(final HttpHost proxy) { this.proxy = proxy; return this; } /** * Determines the name of the cookie specification to be used for HTTP state * management. *

* Default: {@code null} *

*/ public Builder setCookieSpec(final String cookieSpec) { this.cookieSpec = cookieSpec; return this; } /** * Determines whether redirects should be handled automatically. *

* Default: {@code true} *

*/ public Builder setRedirectsEnabled(final boolean redirectsEnabled) { this.redirectsEnabled = redirectsEnabled; return this; } /** * Determines whether circular redirects (redirects to the same location) should * be allowed. The HTTP spec is not sufficiently clear whether circular redirects * are permitted, therefore optionally they can be enabled *

* Default: {@code false} *

*/ public Builder setCircularRedirectsAllowed(final boolean circularRedirectsAllowed) { this.circularRedirectsAllowed = circularRedirectsAllowed; return this; } /** * Returns the maximum number of redirects to be followed. The limit on number * of redirects is intended to prevent infinite loops. *

* Default: {@code 50} *

*/ public Builder setMaxRedirects(final int maxRedirects) { this.maxRedirects = maxRedirects; return this; } /** * Determines whether authentication should be handled automatically. *

* Default: {@code true} *

*/ public Builder setAuthenticationEnabled(final boolean authenticationEnabled) { this.authenticationEnabled = authenticationEnabled; return this; } /** * Determines the order of preference for supported authentication schemes * by their names when authenticating with the target host. *

* Default: {@code null} *

*/ public Builder setTargetPreferredAuthSchemes(final Collection targetPreferredAuthSchemes) { this.targetPreferredAuthSchemes = targetPreferredAuthSchemes; return this; } /** * Determines the order of preference for supported authentication schemes * by their names when authenticating with the proxy host. *

* Default: {@code null} *

*/ public Builder setProxyPreferredAuthSchemes(final Collection proxyPreferredAuthSchemes) { this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes; return this; } /** * Returns the connection lease request timeout used when requesting * a connection from the connection manager. *

* A timeout value of zero is interpreted as an infinite timeout. *

*

* Default: 3 minutes. *

*/ public Builder setConnectionRequestTimeout(final Timeout connectionRequestTimeout) { this.connectionRequestTimeout = connectionRequestTimeout; return this; } /** * @see #setConnectionRequestTimeout(Timeout) */ public Builder setConnectionRequestTimeout(final long connectionRequestTimeout, final TimeUnit timeUnit) { this.connectionRequestTimeout = Timeout.of(connectionRequestTimeout, timeUnit); return this; } /** * Determines the timeout until a new connection is fully established. * This may also include transport security negotiation exchanges * such as {@code SSL} or {@code TLS} protocol negotiation). *

* A timeout value of zero is interpreted as an infinite timeout. *

*

* Default: 3 minutes *

* * @deprecated Use {@link ConnectionConfig.Builder#setConnectTimeout(Timeout)}. */ @Deprecated public Builder setConnectTimeout(final Timeout connectTimeout) { this.connectTimeout = connectTimeout; return this; } /** * @see #setConnectTimeout(Timeout) * * @deprecated Use {@link ConnectionConfig.Builder#setConnectTimeout(long, TimeUnit)}. */ @Deprecated public Builder setConnectTimeout(final long connectTimeout, final TimeUnit timeUnit) { this.connectTimeout = Timeout.of(connectTimeout, timeUnit); return this; } /** * Determines the timeout until arrival of a response from the opposite * endpoint. *

* A timeout value of zero is interpreted as an infinite timeout. *

*

* Please note that response timeout may be unsupported by * HTTP transports with message multiplexing. *

*

* Default: {@code null} *

* * @since 5.0 */ public Builder setResponseTimeout(final Timeout responseTimeout) { this.responseTimeout = responseTimeout; return this; } /** * @see #setResponseTimeout(Timeout) */ public Builder setResponseTimeout(final long responseTimeout, final TimeUnit timeUnit) { this.responseTimeout = Timeout.of(responseTimeout, timeUnit); return this; } /** * Determines the default of value of connection keep-alive time period when not * explicitly communicated by the origin server with a {@code Keep-Alive} response * header. *

* A negative value is interpreted as an infinite keep-alive period. *

*

* Default: 3 minutes *

* * @since 5.0 */ public Builder setConnectionKeepAlive(final TimeValue connectionKeepAlive) { this.connectionKeepAlive = connectionKeepAlive; return this; } /** * @see #setConnectionKeepAlive(TimeValue) */ public Builder setDefaultKeepAlive(final long defaultKeepAlive, final TimeUnit timeUnit) { this.connectionKeepAlive = TimeValue.of(defaultKeepAlive, timeUnit); return this; } /** * Determines whether the target server is requested to compress content. *

* Default: {@code true} *

* * @since 4.5 */ public Builder setContentCompressionEnabled(final boolean contentCompressionEnabled) { this.contentCompressionEnabled = contentCompressionEnabled; return this; } /** * Determines whether request cancellation, such as through {@code * Future#cancel(boolean)}, should kill the underlying connection. If this * option is set to false, the client will attempt to preserve the * underlying connection by allowing the request to complete in the * background, discarding the response. *

* Note that when this option is {@code true}, cancelling a request may * cause other requests to fail, if they are waiting to use the same * connection. *

*

* On HTTP/2, this option has no effect. Request cancellation will always * result in the stream being cancelled with a {@code RST_STREAM}. This * has no effect on connection reuse. *

*

* On non-asynchronous clients, this option has no effect. Request * cancellation, such as through {@code HttpUriRequestBase#cancel()}, will * always kill the underlying connection. *

*

* Default: {@code true} *

* * @since 5.0 */ public Builder setHardCancellationEnabled(final boolean hardCancellationEnabled) { this.hardCancellationEnabled = hardCancellationEnabled; return this; } public RequestConfig build() { return new RequestConfig( expectContinueEnabled, proxy, cookieSpec, redirectsEnabled, circularRedirectsAllowed, maxRedirects, authenticationEnabled, targetPreferredAuthSchemes, proxyPreferredAuthSchemes, connectionRequestTimeout != null ? connectionRequestTimeout : DEFAULT_CONNECTION_REQUEST_TIMEOUT, connectTimeout, responseTimeout, connectionKeepAlive != null ? connectionKeepAlive : DEFAULT_CONN_KEEP_ALIVE, contentCompressionEnabled, hardCancellationEnabled); } } } TlsConfig.java000066400000000000000000000163751434266521000346110ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/config/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.config; import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.util.Timeout; /** * Immutable class encapsulating TLS protocol settings. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public class TlsConfig implements Cloneable { public static final TlsConfig DEFAULT = new Builder().build(); private final Timeout handshakeTimeout; private final String[] supportedProtocols; private final String[] supportedCipherSuites; private final HttpVersionPolicy httpVersionPolicy; /** * Intended for CDI compatibility */ protected TlsConfig() { this(null, null, null, null); } TlsConfig( final Timeout handshakeTimeout, final String[] supportedProtocols, final String[] supportedCipherSuites, final HttpVersionPolicy httpVersionPolicy) { super(); this.handshakeTimeout = handshakeTimeout; this.supportedProtocols = supportedProtocols; this.supportedCipherSuites = supportedCipherSuites; this.httpVersionPolicy = httpVersionPolicy; } /** * @see Builder#setHandshakeTimeout(Timeout) */ public Timeout getHandshakeTimeout() { return handshakeTimeout; } /** * @see Builder#setSupportedProtocols(String...) */ public String[] getSupportedProtocols() { return supportedProtocols != null ? supportedProtocols.clone() : null; } /** * @see Builder#setSupportedCipherSuites(String...) */ public String[] getSupportedCipherSuites() { return supportedCipherSuites != null ? supportedCipherSuites.clone() : null; } /** * @see Builder#setVersionPolicy(HttpVersionPolicy) */ public HttpVersionPolicy getHttpVersionPolicy() { return httpVersionPolicy; } @Override protected TlsConfig clone() throws CloneNotSupportedException { return (TlsConfig) super.clone(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("["); builder.append("handshakeTimeout=").append(handshakeTimeout); builder.append(", supportedProtocols=").append(Arrays.toString(supportedProtocols)); builder.append(", supportedCipherSuites=").append(Arrays.toString(supportedCipherSuites)); builder.append(", httpVersionPolicy=").append(httpVersionPolicy); builder.append("]"); return builder.toString(); } public static TlsConfig.Builder custom() { return new Builder(); } public static TlsConfig.Builder copy(final TlsConfig config) { return new Builder() .setHandshakeTimeout(config.getHandshakeTimeout()) .setSupportedProtocols(config.getSupportedProtocols()) .setSupportedCipherSuites(config.getSupportedCipherSuites()) .setVersionPolicy(config.getHttpVersionPolicy()); } public static class Builder { private Timeout handshakeTimeout; private String[] supportedProtocols; private String[] supportedCipherSuites; private HttpVersionPolicy versionPolicy; /** * Determines the timeout used by TLS session negotiation exchanges (session handshake). *

* A timeout value of zero is interpreted as an infinite timeout. *

*

* Default: {@code null} (undefined) *

*/ public Builder setHandshakeTimeout(final Timeout handshakeTimeout) { this.handshakeTimeout = handshakeTimeout; return this; } /** * @see #setHandshakeTimeout(Timeout) */ public Builder setHandshakeTimeout(final long handshakeTimeout, final TimeUnit timeUnit) { this.handshakeTimeout = Timeout.of(handshakeTimeout, timeUnit); return this; } /** * Determines supported TLS protocols. *

* Default: {@code null} (undefined) *

*/ public Builder setSupportedProtocols(final String... supportedProtocols) { this.supportedProtocols = supportedProtocols; return this; } /** * Determines supported TLS protocols. *

* Default: {@code null} (undefined) *

*/ public Builder setSupportedProtocols(final TLS... supportedProtocols) { this.supportedProtocols = new String[supportedProtocols.length]; for (int i = 0; i < supportedProtocols.length; i++) { final TLS protocol = supportedProtocols[i]; if (protocol != null) { this.supportedProtocols[i] = protocol.id; } } return this; } /** * Determines supported cipher suites. *

* Default: {@code null} (undefined) *

*/ public Builder setSupportedCipherSuites(final String... supportedCipherSuites) { this.supportedCipherSuites = supportedCipherSuites; return this; } /** * Determines the HTTP protocol policy. By default, connections are expected to use TLS ALPN * extension to negotiate the application protocol to be used by both endpoints. *

* Default: {@link HttpVersionPolicy#NEGOTIATE} *

*/ public Builder setVersionPolicy(final HttpVersionPolicy versionPolicy) { this.versionPolicy = versionPolicy; return this; } public TlsConfig build() { return new TlsConfig( handshakeTimeout, supportedProtocols, supportedCipherSuites, versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE); } } } package-info.java000066400000000000000000000023671434266521000352410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/config/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP client configuration APIs. */ package org.apache.hc.client5.http.config; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/000077500000000000000000000000001434266521000321255ustar00rootroot00000000000000BasicCookieStore.java000066400000000000000000000144041434266521000361040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.TreeSet; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Default implementation of {@link CookieStore} * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class BasicCookieStore implements CookieStore, Serializable { private static final long serialVersionUID = -7581093305228232025L; private final TreeSet cookies; private transient ReadWriteLock lock; public BasicCookieStore() { super(); this.cookies = new TreeSet<>(CookieIdentityComparator.INSTANCE); this.lock = new ReentrantReadWriteLock(); } private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); /* Reinstantiate transient fields. */ this.lock = new ReentrantReadWriteLock(); } /** * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies. * If the given cookie has already expired it will not be added, but existing * values will still be removed. * * @param cookie the {@link Cookie cookie} to be added * * @see #addCookies(Cookie[]) * */ @Override public void addCookie(final Cookie cookie) { if (cookie != null) { lock.writeLock().lock(); try { // first remove any old cookie that is equivalent cookies.remove(cookie); if (!cookie.isExpired(Instant.now())) { cookies.add(cookie); } } finally { lock.writeLock().unlock(); } } } /** * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and * in the given array order. If any of the given cookies has already expired it will * not be added, but existing values will still be removed. * * @param cookies the {@link Cookie cookies} to be added * * @see #addCookie(Cookie) * */ public void addCookies(final Cookie[] cookies) { if (cookies != null) { for (final Cookie cookie : cookies) { this.addCookie(cookie); } } } /** * Returns an immutable array of {@link Cookie cookies} that this HTTP * state currently contains. * * @return an array of {@link Cookie cookies}. */ @Override public List getCookies() { lock.readLock().lock(); try { //create defensive copy so it won't be concurrently modified return new ArrayList<>(cookies); } finally { lock.readLock().unlock(); } } /** * Removes all of {@link Cookie cookies} in this HTTP state * that have expired by the specified {@link java.util.Date date}. * * @return true if any cookies were purged. * * @see Cookie#isExpired(Date) */ @Override @SuppressWarnings("deprecation") public boolean clearExpired(final Date date) { if (date == null) { return false; } lock.writeLock().lock(); try { boolean removed = false; for (final Iterator it = cookies.iterator(); it.hasNext(); ) { if (it.next().isExpired(date)) { it.remove(); removed = true; } } return removed; } finally { lock.writeLock().unlock(); } } /** * Removes all of {@link Cookie cookies} in this HTTP state that have expired by the specified * {@link Instant date}. * * @return true if any cookies were purged. * @see Cookie#isExpired(Instant) * @since 5.2 */ @Override public boolean clearExpired(final Instant instant) { if (instant == null) { return false; } lock.writeLock().lock(); try { boolean removed = false; for (final Iterator it = cookies.iterator(); it.hasNext(); ) { if (it.next().isExpired(instant)) { it.remove(); removed = true; } } return removed; } finally { lock.writeLock().unlock(); } } /** * Clears all cookies. */ @Override public void clear() { lock.writeLock().lock(); try { cookies.clear(); } finally { lock.writeLock().unlock(); } } @Override public String toString() { lock.readLock().lock(); try { return cookies.toString(); } finally { lock.readLock().unlock(); } } } CommonCookieAttributeHandler.java000066400000000000000000000030371434266521000404600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; /** * Extension of {@link org.apache.hc.client5.http.cookie.CookieAttributeHandler} intended * to handle one specific common attribute whose name is returned with * {@link #getAttributeName()} method. * * @since 4.4 */ public interface CommonCookieAttributeHandler extends CookieAttributeHandler { String getAttributeName(); } Cookie.java000066400000000000000000000127131434266521000341260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.time.Instant; import java.util.Date; /** * Cookie interface represents a token or short packet of state information * (also referred to as "magic-cookie") that the HTTP agent and the target * server can exchange to maintain a session. In its simples form an HTTP * cookie is merely a name / value pair. * * @since 4.0 */ public interface Cookie { String PATH_ATTR = "path"; String DOMAIN_ATTR = "domain"; String MAX_AGE_ATTR = "max-age"; String SECURE_ATTR = "secure"; String EXPIRES_ATTR = "expires"; String HTTP_ONLY_ATTR = "httpOnly"; /** * @since 5.0 */ String getAttribute(String name); /** * @since 5.0 */ boolean containsAttribute(String name); /** * Returns the name. * * @return String name The name */ String getName(); /** * Returns the value. * * @return String value The current value. */ String getValue(); /** * Returns the expiration {@link Date} of the cookie, or {@code null} * if none exists. *

Note: the object returned by this method is * considered immutable. Changing it (e.g. using setTime()) could result * in undefined behaviour. Do so at your peril.

* @return Expiration {@link Date}, or {@code null}. * @deprecated Use {{@link #getExpiryInstant()}} */ @Deprecated Date getExpiryDate(); /** * Returns the expiration {@link Instant} of the cookie, or {@code null} if none exists. *

Note: the object returned by this method is * considered immutable. Changing it (e.g. using setTime()) could result in undefined behaviour. * Do so at your peril.

* * @return Expiration {@link Instant}, or {@code null}. * @since 5.2 */ @SuppressWarnings("deprecated") default Instant getExpiryInstant() { final Date date = getExpiryDate(); return date != null ? Instant.ofEpochMilli(date.getTime()) : null; } /** * Returns {@code false} if the cookie should be discarded at the end * of the "session"; {@code true} otherwise. * * @return {@code false} if the cookie should be discarded at the end * of the "session"; {@code true} otherwise */ boolean isPersistent(); /** * Returns domain attribute of the cookie. The value of the Domain * attribute specifies the domain for which the cookie is valid. * * @return the value of the domain attribute. */ String getDomain(); /** * Returns the path attribute of the cookie. The value of the Path * attribute specifies the subset of URLs on the origin server to which * this cookie applies. * * @return The value of the path attribute. */ String getPath(); /** * Indicates whether this cookie requires a secure connection. * * @return {@code true} if this cookie should only be sent * over secure connections, {@code false} otherwise. */ boolean isSecure(); /** * Returns true if this cookie has expired. * @param date Current time * * @return {@code true} if the cookie has expired. * @deprecated Use {{@link #isExpired(Instant)}} */ @Deprecated boolean isExpired(final Date date); /** * Returns true if this cookie has expired. * * @param date Current time * @return {@code true} if the cookie has expired. * @since 5.2 */ @SuppressWarnings("deprecation") default boolean isExpired(final Instant date) { return isExpired(date != null ? new Date(date.toEpochMilli()) : null); } /** * Returns creation time of the cookie. * @deprecated Use {@link #getCreationInstant()} */ @Deprecated Date getCreationDate(); /** * Returns creation time of the cookie. */ default Instant getCreationInstant() { return null; } /** * Checks whether this Cookie has been marked as {@code httpOnly}. *

The default implementation returns {@code false}. * * @return true if this Cookie has been marked as {@code httpOnly}, * false otherwise * * @since 5.2 */ default boolean isHttpOnly(){ return false; } } CookieAttributeHandler.java000066400000000000000000000060151434266521000373060ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * This interface represents a cookie attribute handler responsible * for parsing, validating, and matching a specific cookie attribute, * such as path, domain, port, etc. *

* Different cookie specifications can provide a specific * implementation for this class based on their cookie handling * rules. *

* * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface CookieAttributeHandler { /** * Parse the given cookie attribute value and processChallenge the corresponding * {@link org.apache.hc.client5.http.cookie.Cookie} property. * * @param cookie {@link org.apache.hc.client5.http.cookie.Cookie} to be updated * @param value cookie attribute value from the cookie response header * @throws MalformedCookieException if cookie parsing fails for this attribute */ void parse(SetCookie cookie, String value) throws MalformedCookieException; /** * Performs cookie validation for the given attribute value. * * @param cookie {@link org.apache.hc.client5.http.cookie.Cookie} to validate * @param origin the cookie source to validate against * @throws MalformedCookieException if cookie validation fails for this attribute */ void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException; /** * Matches the given value (property of the destination host where request is being * submitted) with the corresponding cookie attribute. * * @param cookie {@link org.apache.hc.client5.http.cookie.Cookie} to match * @param origin the cookie source to match against * @return {@code true} if the match is successful; {@code false} otherwise */ boolean match(Cookie cookie, CookieOrigin origin); } CookieIdentityComparator.java000066400000000000000000000053431434266521000376710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.io.Serializable; import java.util.Comparator; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * This cookie comparator can be used to compare identity of cookies. *

* Cookies are considered identical if their names are equal and * their domain attributes match ignoring case. *

* * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class CookieIdentityComparator implements Serializable, Comparator { /** * Singleton instance. * * @since 5.2 */ public static final CookieIdentityComparator INSTANCE = new CookieIdentityComparator(); private static final long serialVersionUID = 4466565437490631532L; @Override public int compare(final Cookie c1, final Cookie c2) { int res = c1.getName().compareTo(c2.getName()); if (res == 0) { // do not differentiate empty and null domains String d1 = c1.getDomain(); if (d1 == null) { d1 = ""; } String d2 = c2.getDomain(); if (d2 == null) { d2 = ""; } res = d1.compareToIgnoreCase(d2); } if (res == 0) { String p1 = c1.getPath(); if (p1 == null) { p1 = "/"; } String p2 = c2.getPath(); if (p2 == null) { p2 = "/"; } res = p1.compareTo(p2); } return res; } } CookieOrigin.java000066400000000000000000000056011434266521000352740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.util.Locale; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * CookieOrigin class encapsulates details of an origin server that * are relevant when parsing, validating or matching HTTP cookies. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class CookieOrigin { private final String host; private final int port; private final String path; private final boolean secure; public CookieOrigin(final String host, final int port, final String path, final boolean secure) { super(); Args.notBlank(host, "Host"); Args.notNegative(port, "Port"); Args.notNull(path, "Path"); this.host = host.toLowerCase(Locale.ROOT); this.port = port; if (!TextUtils.isBlank(path)) { this.path = path; } else { this.path = "/"; } this.secure = secure; } public String getHost() { return this.host; } public String getPath() { return this.path; } public int getPort() { return this.port; } public boolean isSecure() { return this.secure; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append('['); if (this.secure) { buffer.append("(secure)"); } buffer.append(this.host); buffer.append(':'); buffer.append(this.port); buffer.append(this.path); buffer.append(']'); return buffer.toString(); } } CookiePathComparator.java000066400000000000000000000054121434266521000367710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.io.Serializable; import java.util.Comparator; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * This cookie comparator ensures that multiple cookies satisfying * a common criteria are ordered in the {@link Cookie} header such * that those with more specific Path attributes precede those with * less specific. * *

* This comparator assumes that Path attributes of two cookies * path-match a common request-URI. Otherwise, the result of the * comparison is undefined. *

* * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class CookiePathComparator implements Serializable, Comparator { public static final CookiePathComparator INSTANCE = new CookiePathComparator(); private static final long serialVersionUID = 7523645369616405818L; private String normalizePath(final Cookie cookie) { String path = cookie.getPath(); if (path == null) { path = "/"; } if (!path.endsWith("/")) { path = path + '/'; } return path; } @Override public int compare(final Cookie c1, final Cookie c2) { final String path1 = normalizePath(c1); final String path2 = normalizePath(c2); if (path1.equals(path2)) { return 0; } else if (path1.startsWith(path2)) { return -1; } else if (path2.startsWith(path1)) { return 1; } else { // Does not really matter return 0; } } } CookiePriorityComparator.java000066400000000000000000000047101434266521000377160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.time.Instant; import java.util.Comparator; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * This cookie comparator ensures that cookies with longer paths take precedence over * cookies with shorter path. Among cookies with equal path length cookies with earlier * creation time take precedence over cookies with later creation time * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public class CookiePriorityComparator implements Comparator { public static final CookiePriorityComparator INSTANCE = new CookiePriorityComparator(); private int getPathLength(final Cookie cookie) { final String path = cookie.getPath(); return path != null ? path.length() : 1; } @Override public int compare(final Cookie c1, final Cookie c2) { final int l1 = getPathLength(c1); final int l2 = getPathLength(c2); final int result = l2 - l1; if (result == 0) { final Instant d1 = c1.getCreationInstant(); final Instant d2 = c2.getCreationInstant(); if (d1 != null && d2 != null) { return d1.compareTo(d2); } } return result; } } CookieRestrictionViolationException.java000066400000000000000000000036461434266521000421250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; /** * Signals that a cookie violates a restriction imposed by the cookie * specification. * * @since 4.1 */ public class CookieRestrictionViolationException extends MalformedCookieException { private static final long serialVersionUID = 7371235577078589013L; /** * Creates a new CookeFormatViolationException with a {@code null} detail * message. */ public CookieRestrictionViolationException() { super(); } /** * Creates a new CookeRestrictionViolationException with a specified * message string. * * @param message The exception detail message */ public CookieRestrictionViolationException(final String message) { super(message); } } CookieSpec.java000066400000000000000000000063671434266521000347510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.util.List; import org.apache.hc.core5.http.Header; /** * Defines the cookie management specification. *

Cookie management specification must define *

    *
  • rules of parsing "Set-Cookie" header *
  • rules of validation of parsed cookies *
  • formatting of "Cookie" header *
* for a given host, port and path of origin * * @since 4.0 */ public interface CookieSpec { /** * Parse the {@code "Set-Cookie"} Header into an array of Cookies. * *

This method will not perform the validation of the resultant * {@link Cookie}s

* * @see #validate * * @param header the {@code Set-Cookie} received from the server * @param origin details of the cookie origin * @return an array of {@code Cookie}s parsed from the header * @throws MalformedCookieException if an exception occurs during parsing */ List parse(Header header, CookieOrigin origin) throws MalformedCookieException; /** * Validate the cookie according to validation rules defined by the * cookie specification. * * @param cookie the Cookie to validate * @param origin details of the cookie origin * @throws MalformedCookieException if the cookie is invalid */ void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException; /** * Determines if a Cookie matches the target location. * * @param cookie the Cookie to be matched * @param origin the target to test against * * @return {@code true} if the cookie should be submitted with a request * with given attributes, {@code false} otherwise. */ boolean match(Cookie cookie, CookieOrigin origin); /** * Create {@code "Cookie"} headers for an array of Cookies. * * @param cookies the Cookies format into a Cookie header * @return a Header for the given Cookies. * @throws IllegalArgumentException if an input parameter is illegal */ List
formatCookies(List cookies); } CookieSpecFactory.java000066400000000000000000000032461434266521000362720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * Factory for {@link CookieSpec} implementations. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface CookieSpecFactory { /** * Creates an instance of {@link CookieSpec}. * * @return auth scheme. */ CookieSpec create(HttpContext context); } CookieStore.java000066400000000000000000000050501434266521000351370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.time.Instant; import java.util.Date; import java.util.List; /** * This interface represents an abstract store for {@link Cookie} * objects. * * @since 4.0 */ public interface CookieStore { /** * Adds an {@link Cookie}, replacing any existing equivalent cookies. * If the given cookie has already expired it will not be added, but existing * values will still be removed. * * @param cookie the {@link Cookie cookie} to be added */ void addCookie(Cookie cookie); /** * Returns all cookies contained in this store. * * @return all cookies */ List getCookies(); /** * Removes all of {@link Cookie}s in this store that have expired by * the specified {@link java.util.Date}. * * @return true if any cookies were purged. * @deprecated Use {@link #clearExpired(Instant)} */ @Deprecated boolean clearExpired(Date date); /** * Removes all of {@link Cookie}s in this store that have expired by * the specified {@link Instant}. * * @return true if any cookies were purged. */ @SuppressWarnings("deprecation") default boolean clearExpired(Instant date) { return clearExpired(date != null ? new Date(date.toEpochMilli()) : null); } /** * Clears all cookies. */ void clear(); } MalformedCookieException.java000066400000000000000000000045121434266521000376320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import org.apache.hc.core5.http.ProtocolException; /** * Signals that a cookie is in some way invalid or illegal in a given * context * * @since 4.0 */ public class MalformedCookieException extends ProtocolException { private static final long serialVersionUID = -6695462944287282185L; /** * Creates a new MalformedCookieException with a {@code null} detail message. */ public MalformedCookieException() { super(); } /** * Creates a new MalformedCookieException with a specified message string. * * @param message The exception detail message */ public MalformedCookieException(final String message) { super(message); } /** * Creates a new MalformedCookieException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public MalformedCookieException(final String message, final Throwable cause) { super(message, cause); } } SetCookie.java000066400000000000000000000071721434266521000346050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.time.Instant; import java.util.Date; import org.apache.hc.client5.http.utils.DateUtils; /** * This interface represents a {@code Set-Cookie} response header sent by the * origin server to the HTTP agent in order to maintain a conversational state. * * @since 4.0 */ public interface SetCookie extends Cookie { void setValue(String value); /** * Sets expiration date. *

Note: the object returned by this method is considered * immutable. Changing it (e.g. using setTime()) could result in undefined * behaviour. Do so at your peril.

* * @param expiryDate the {@link Date} after which this cookie is no longer valid. * * @see Cookie#getExpiryDate * @deprecated Use {{@link #setExpiryDate(Instant)}} */ @Deprecated void setExpiryDate (Date expiryDate); /** * Sets expiration date. *

Note: the object returned by this method is considered * immutable. Changing it (e.g. using setTime()) could result in undefined behaviour. Do so at * your peril.

* * @param expiryDate the {@link Instant} after which this cookie is no longer valid. * @see Cookie#getExpiryInstant() * @since 5.2 */ @SuppressWarnings("deprecated") default void setExpiryDate(Instant expiryDate) { setExpiryDate(DateUtils.toDate(expiryDate)); } /** * Sets the domain attribute. * * @param domain The value of the domain attribute * * @see Cookie#getDomain */ void setDomain(String domain); /** * Sets the path attribute. * * @param path The value of the path attribute * * @see Cookie#getPath * */ void setPath(String path); /** * Sets the secure attribute of the cookie. *

* When {@code true} the cookie should only be sent * using a secure protocol (https). This should only be set when * the cookie's originating server used a secure protocol to set the * cookie's value. * * @param secure The value of the secure attribute * * @see #isSecure() */ void setSecure (boolean secure); /** * Marks or unmarks this Cookie as {@code httpOnly}. * * @param httpOnly true if this cookie is to be marked as * {@code httpOnly}, false otherwise * * @since 5.2 */ default void setHttpOnly (final boolean httpOnly){ } } StandardCookieSpec.java000066400000000000000000000034341434266521000364220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; /** * Cookie specifications by their names supported by the HttpClient. * * @since 4.3 */ public final class StandardCookieSpec { private StandardCookieSpec() { // no instances } /** * The RFC 6265 compliant policy (interoperability profile). */ public static final String RELAXED = "relaxed"; /** * The RFC 6265 compliant policy (strict profile). * * @since 4.4 */ public static final String STRICT = "strict"; /** * The policy that ignores cookies. */ public static final String IGNORE = "ignore"; } package-info.java000066400000000000000000000023721434266521000352410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client HTTP state management APIs. */ package org.apache.hc.client5.http.cookie; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/000077500000000000000000000000001434266521000321705ustar00rootroot00000000000000BrotliDecompressingEntity.java000066400000000000000000000040011434266521000401220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import org.apache.hc.core5.http.HttpEntity; /** * {@link org.apache.hc.core5.http.io.entity.HttpEntityWrapper} responsible for * handling br Content Coded responses. * * @see GzipDecompressingEntity * @since 5.2 */ public class BrotliDecompressingEntity extends DecompressingEntity { /** * Creates a new {@link DecompressingEntity}. * * @param entity factory to create decompressing stream. */ public BrotliDecompressingEntity(final HttpEntity entity) { super(entity, BrotliInputStreamFactory.getInstance()); } public static boolean isAvailable() { try { Class.forName("org.brotli.dec.BrotliInputStream"); return true; } catch (final ClassNotFoundException | NoClassDefFoundError e) { return false; } } } BrotliInputStreamFactory.java000066400000000000000000000037121434266521000377360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.brotli.dec.BrotliInputStream; /** * {@link InputStreamFactory} for handling Brotli Content Coded responses. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BrotliInputStreamFactory implements InputStreamFactory { private static final BrotliInputStreamFactory INSTANCE = new BrotliInputStreamFactory(); public static BrotliInputStreamFactory getInstance() { return INSTANCE; } @Override public InputStream create(final InputStream inputStream) throws IOException { return new BrotliInputStream(inputStream); } } DecompressingEntity.java000066400000000000000000000067141434266521000367630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.HttpEntityWrapper; import org.apache.hc.core5.util.Args; /** * Common base class for decompressing {@link HttpEntity} implementations. * * @since 4.4 */ public class DecompressingEntity extends HttpEntityWrapper { /** * Default buffer size. */ private static final int BUFFER_SIZE = 1024 * 2; private final InputStreamFactory inputStreamFactory; /** * {@link #getContent()} method must return the same {@link InputStream} * instance when DecompressingEntity is wrapping a streaming entity. */ private InputStream content; /** * Creates a new {@link DecompressingEntity}. * * @param wrapped the non-null {@link HttpEntity} to be wrapped * @param inputStreamFactory factory to create decompressing stream. */ public DecompressingEntity( final HttpEntity wrapped, final InputStreamFactory inputStreamFactory) { super(wrapped); this.inputStreamFactory = inputStreamFactory; } private InputStream getDecompressingStream() throws IOException { return new LazyDecompressingInputStream(super.getContent(), inputStreamFactory); } @Override public InputStream getContent() throws IOException { if (super.isStreaming()) { if (content == null) { content = getDecompressingStream(); } return content; } return getDecompressingStream(); } @Override public void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); try (InputStream inStream = getContent()) { final byte[] buffer = new byte[BUFFER_SIZE]; int l; while ((l = inStream.read(buffer)) != -1) { outStream.write(buffer, 0, l); } } } @Override public String getContentEncoding() { /* Content encoding is now 'identity' */ return null; } @Override public long getContentLength() { /* length of decompressed content is not known */ return -1; } } DeflateDecompressingEntity.java000066400000000000000000000045331434266521000402450ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import org.apache.hc.core5.http.HttpEntity; /** * {@link org.apache.hc.core5.http.io.entity.HttpEntityWrapper} responsible for * handling deflate Content Coded responses. In RFC2616 terms, {@code deflate} * means a {@code zlib} stream as defined in RFC1950. Some server * implementations have misinterpreted RFC2616 to mean that a * {@code deflate} stream as defined in RFC1951 should be used * (or maybe they did that since that's how IE behaves?). It's confusing * that {@code deflate} in HTTP 1.1 means {@code zlib} streams * rather than {@code deflate} streams. We handle both types in here, * since that's what is seen on the internet. Moral - prefer * {@code gzip}! * * @see GzipDecompressingEntity * * @since 4.1 */ public class DeflateDecompressingEntity extends DecompressingEntity { /** * Creates a new {@link DeflateDecompressingEntity} which will wrap the specified * {@link HttpEntity}. * * @param entity * a non-null {@link HttpEntity} to be wrapped */ public DeflateDecompressingEntity(final HttpEntity entity) { super(entity, DeflateInputStreamFactory.getInstance()); } } DeflateInputStream.java000066400000000000000000000102701434266521000365140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import java.util.zip.ZipException; /** * Deflate input stream. This class includes logic needed for various Rfc's in order * to reasonably implement the "deflate" compression style. */ public class DeflateInputStream extends InputStream { private final InputStream sourceStream; public DeflateInputStream(final InputStream wrapped) throws IOException { final PushbackInputStream pushback = new PushbackInputStream(wrapped, 2); final int i1 = pushback.read(); final int i2 = pushback.read(); if (i1 == -1 || i2 == -1) { throw new ZipException("Unexpected end of stream"); } pushback.unread(i2); pushback.unread(i1); boolean nowrap = true; final int b1 = i1 & 0xFF; final int compressionMethod = b1 & 0xF; final int compressionInfo = b1 >> 4 & 0xF; final int b2 = i2 & 0xFF; if (compressionMethod == 8 && compressionInfo <= 7 && ((b1 << 8) | b2) % 31 == 0) { nowrap = false; } sourceStream = new DeflateStream(pushback, new Inflater(nowrap)); } /** * Read a byte. */ @Override public int read() throws IOException { return sourceStream.read(); } /** * Read lots of bytes. */ @Override public int read(final byte[] b) throws IOException { return sourceStream.read(b); } /** * Read lots of specific bytes. */ @Override public int read(final byte[] b, final int off, final int len) throws IOException { return sourceStream.read(b, off, len); } /** * Skip */ @Override public long skip(final long n) throws IOException { return sourceStream.skip(n); } /** * Get available. */ @Override public int available() throws IOException { return sourceStream.available(); } /** * Mark. */ @Override public void mark(final int readLimit) { sourceStream.mark(readLimit); } /** * Reset. */ @Override public void reset() throws IOException { sourceStream.reset(); } /** * Check if mark is supported. */ @Override public boolean markSupported() { return sourceStream.markSupported(); } /** * Close. */ @Override public void close() throws IOException { sourceStream.close(); } static class DeflateStream extends InflaterInputStream { private boolean closed; public DeflateStream(final InputStream in, final Inflater inflater) { super(in, inflater); } @Override public void close() throws IOException { if (closed) { return; } closed = true; inf.end(); super.close(); } } } DeflateInputStreamFactory.java000066400000000000000000000040661434266521000400520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * {@link InputStreamFactory} for handling Deflate Content Coded responses. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DeflateInputStreamFactory implements InputStreamFactory { /** * Singleton instance. */ private static final DeflateInputStreamFactory INSTANCE = new DeflateInputStreamFactory(); /** * Gets the singleton instance. * * @return the singleton instance. */ public static DeflateInputStreamFactory getInstance() { return INSTANCE; } @Override public InputStream create(final InputStream inputStream) throws IOException { return new DeflateInputStream(inputStream); } } EntityBuilder.java000066400000000000000000000251701434266521000355440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.File; import java.io.InputStream; import java.io.Serializable; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.FileEntity; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.SerializableEntity; import org.apache.hc.core5.http.io.entity.StringEntity; /** * Builder for {@link HttpEntity} instances. *

* Several setter methods of this builder are mutually exclusive. In case of multiple invocations * of the following methods only the last one will have effect: *

*
    *
  • {@link #setText(String)}
  • *
  • {@link #setBinary(byte[])}
  • *
  • {@link #setStream(java.io.InputStream)}
  • *
  • {@link #setSerializable(java.io.Serializable)}
  • *
  • {@link #setParameters(java.util.List)}
  • *
  • {@link #setParameters(NameValuePair...)}
  • *
  • {@link #setFile(java.io.File)}
  • *
* * @since 4.3 */ public class EntityBuilder { private String text; private byte[] binary; private InputStream stream; private List parameters; private Serializable serializable; private File file; private ContentType contentType; private String contentEncoding; private boolean chunked; private boolean gzipCompressed; EntityBuilder() { super(); } public static EntityBuilder create() { return new EntityBuilder(); } private void clearContent() { this.text = null; this.binary = null; this.stream = null; this.parameters = null; this.serializable = null; this.file = null; } /** * Returns entity content as a string if set using {@link #setText(String)} method. */ public String getText() { return text; } /** * Sets entity content as a string. This method is mutually exclusive with * {@link #setBinary(byte[])}, * {@link #setStream(java.io.InputStream)} , * {@link #setSerializable(java.io.Serializable)} , * {@link #setParameters(java.util.List)}, * {@link #setParameters(NameValuePair...)} * {@link #setFile(java.io.File)} methods. */ public EntityBuilder setText(final String text) { clearContent(); this.text = text; return this; } /** * Returns entity content as a byte array if set using * {@link #setBinary(byte[])} method. */ public byte[] getBinary() { return binary; } /** * Sets entity content as a byte array. This method is mutually exclusive with * {@link #setText(String)}, * {@link #setStream(java.io.InputStream)} , * {@link #setSerializable(java.io.Serializable)} , * {@link #setParameters(java.util.List)}, * {@link #setParameters(NameValuePair...)} * {@link #setFile(java.io.File)} methods. */ public EntityBuilder setBinary(final byte[] binary) { clearContent(); this.binary = binary; return this; } /** * Returns entity content as a {@link InputStream} if set using * {@link #setStream(java.io.InputStream)} method. */ public InputStream getStream() { return stream; } /** * Sets entity content as a {@link InputStream}. This method is mutually exclusive with * {@link #setText(String)}, * {@link #setBinary(byte[])}, * {@link #setSerializable(java.io.Serializable)} , * {@link #setParameters(java.util.List)}, * {@link #setParameters(NameValuePair...)} * {@link #setFile(java.io.File)} methods. */ public EntityBuilder setStream(final InputStream stream) { clearContent(); this.stream = stream; return this; } /** * Returns entity content as a parameter list if set using * {@link #setParameters(java.util.List)} or * {@link #setParameters(NameValuePair...)} methods. */ public List getParameters() { return parameters; } /** * Sets entity content as a parameter list. This method is mutually exclusive with * {@link #setText(String)}, * {@link #setBinary(byte[])}, * {@link #setStream(java.io.InputStream)} , * {@link #setSerializable(java.io.Serializable)} , * {@link #setFile(java.io.File)} methods. */ public EntityBuilder setParameters(final List parameters) { clearContent(); this.parameters = parameters; return this; } /** * Sets entity content as a parameter list. This method is mutually exclusive with * {@link #setText(String)}, * {@link #setBinary(byte[])}, * {@link #setStream(java.io.InputStream)} , * {@link #setSerializable(java.io.Serializable)} , * {@link #setFile(java.io.File)} methods. */ public EntityBuilder setParameters(final NameValuePair... parameters) { return setParameters(Arrays.asList(parameters)); } /** * Returns entity content as a {@link Serializable} if set using * {@link #setSerializable(java.io.Serializable)} method. */ public Serializable getSerializable() { return serializable; } /** * Sets entity content as a {@link Serializable}. This method is mutually exclusive with * {@link #setText(String)}, * {@link #setBinary(byte[])}, * {@link #setStream(java.io.InputStream)} , * {@link #setParameters(java.util.List)}, * {@link #setParameters(NameValuePair...)} * {@link #setFile(java.io.File)} methods. */ public EntityBuilder setSerializable(final Serializable serializable) { clearContent(); this.serializable = serializable; return this; } /** * Returns entity content as a {@link File} if set using * {@link #setFile(java.io.File)} method. */ public File getFile() { return file; } /** * Sets entity content as a {@link File}. This method is mutually exclusive with * {@link #setText(String)}, * {@link #setBinary(byte[])}, * {@link #setStream(java.io.InputStream)} , * {@link #setParameters(java.util.List)}, * {@link #setParameters(NameValuePair...)} * {@link #setSerializable(java.io.Serializable)} methods. */ public EntityBuilder setFile(final File file) { clearContent(); this.file = file; return this; } /** * Returns {@link ContentType} of the entity, if set. */ public ContentType getContentType() { return contentType; } /** * Sets {@link ContentType} of the entity. */ public EntityBuilder setContentType(final ContentType contentType) { this.contentType = contentType; return this; } /** * Returns content encoding of the entity, if set. */ public String getContentEncoding() { return contentEncoding; } /** * Sets content encoding of the entity. */ public EntityBuilder setContentEncoding(final String contentEncoding) { this.contentEncoding = contentEncoding; return this; } /** * Returns {@code true} if entity is to be chunk coded, {@code false} otherwise. */ public boolean isChunked() { return chunked; } /** * Makes entity chunk coded. */ public EntityBuilder chunked() { this.chunked = true; return this; } /** * Returns {@code true} if entity is to be GZIP compressed, {@code false} otherwise. */ public boolean isGzipCompressed() { return gzipCompressed; } /** * Makes entity GZIP compressed. */ public EntityBuilder gzipCompressed() { this.gzipCompressed = true; return this; } private ContentType getContentOrDefault(final ContentType def) { return this.contentType != null ? this.contentType : def; } /** * Creates new instance of {@link HttpEntity} based on the current state. */ public HttpEntity build() { final AbstractHttpEntity e; if (this.text != null) { e = new StringEntity(this.text, getContentOrDefault(ContentType.DEFAULT_TEXT), this.contentEncoding, this.chunked); } else if (this.binary != null) { e = new ByteArrayEntity(this.binary, getContentOrDefault(ContentType.DEFAULT_BINARY), this.contentEncoding, this.chunked); } else if (this.stream != null) { e = new InputStreamEntity(this.stream, -1, getContentOrDefault(ContentType.DEFAULT_BINARY), this.contentEncoding); } else if (this.parameters != null) { e = new UrlEncodedFormEntity(this.parameters, this.contentType != null ? this.contentType.getCharset() : null); } else if (this.serializable != null) { e = new SerializableEntity(this.serializable, ContentType.DEFAULT_BINARY, this.contentEncoding); } else if (this.file != null) { e = new FileEntity(this.file, getContentOrDefault(ContentType.DEFAULT_BINARY), this.contentEncoding); } else { throw new IllegalStateException("No entity set"); } if (this.gzipCompressed) { return new GzipCompressingEntity(e); } return e; } } GZIPInputStreamFactory.java000066400000000000000000000041111434266521000372460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; import java.util.zip.GZIPInputStream; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * {@link InputStreamFactory} for handling GZIPContent Coded responses. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class GZIPInputStreamFactory implements InputStreamFactory { /** * Singleton instance. */ private static final GZIPInputStreamFactory INSTANCE = new GZIPInputStreamFactory(); /** * Gets the singleton instance. * * @return the singleton instance. */ public static GZIPInputStreamFactory getInstance() { return INSTANCE; } @Override public InputStream create(final InputStream inputStream) throws IOException { return new GZIPInputStream(inputStream); } } GzipCompressingEntity.java000066400000000000000000000050301434266521000372720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.GZIPOutputStream; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.HttpEntityWrapper; import org.apache.hc.core5.util.Args; /** * Wrapping entity that compresses content when {@link #writeTo writing}. * * * @since 4.0 */ public class GzipCompressingEntity extends HttpEntityWrapper { private static final String GZIP_CODEC = "gzip"; public GzipCompressingEntity(final HttpEntity entity) { super(entity); } @Override public String getContentEncoding() { return GZIP_CODEC; } @Override public long getContentLength() { return -1; } @Override public boolean isChunked() { // force content chunking return true; } @Override public InputStream getContent() throws IOException { throw new UnsupportedOperationException(); } @Override public void writeTo(final OutputStream outStream) throws IOException { Args.notNull(outStream, "Output stream"); final GZIPOutputStream gzip = new GZIPOutputStream(outStream); super.writeTo(gzip); // Only close output stream if the wrapped entity has been // successfully written out gzip.close(); } } GzipDecompressingEntity.java000066400000000000000000000034421434266521000376100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import org.apache.hc.core5.http.HttpEntity; /** * {@link org.apache.hc.core5.http.io.entity.HttpEntityWrapper} for handling * gzip Content Coded responses. * * @since 4.1 */ public class GzipDecompressingEntity extends DecompressingEntity { /** * Creates a new {@link GzipDecompressingEntity} which will wrap the * specified {@link HttpEntity}. * * @param entity * the non-null {@link HttpEntity} to be wrapped */ public GzipDecompressingEntity(final HttpEntity entity) { super(entity, GZIPInputStreamFactory.getInstance()); } } InputStreamFactory.java000066400000000000000000000027031434266521000365610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; /** * Factory for decorated {@link java.io.InputStream}s. * * @since 4.4 */ public interface InputStreamFactory { InputStream create(InputStream inputStream) throws IOException; } LazyDecompressingInputStream.java000066400000000000000000000056551434266521000406250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.IOException; import java.io.InputStream; /** * Lazy init InputStream wrapper. */ class LazyDecompressingInputStream extends InputStream { private final InputStream wrappedStream; private final InputStreamFactory inputStreamFactory; private InputStream wrapperStream; public LazyDecompressingInputStream( final InputStream wrappedStream, final InputStreamFactory inputStreamFactory) { this.wrappedStream = wrappedStream; this.inputStreamFactory = inputStreamFactory; } private void initWrapper() throws IOException { if (wrapperStream == null) { wrapperStream = inputStreamFactory.create(wrappedStream); } } @Override public int read() throws IOException { initWrapper(); return wrapperStream.read(); } @Override public int read(final byte[] b) throws IOException { initWrapper(); return wrapperStream.read(b); } @Override public int read(final byte[] b, final int off, final int len) throws IOException { initWrapper(); return wrapperStream.read(b, off, len); } @Override public long skip(final long n) throws IOException { initWrapper(); return wrapperStream.skip(n); } @Override public boolean markSupported() { return false; } @Override public int available() throws IOException { initWrapper(); return wrapperStream.available(); } @Override public void close() throws IOException { try { if (wrapperStream != null) { wrapperStream.close(); } } finally { wrappedStream.close(); } } } UrlEncodedFormEntity.java000066400000000000000000000061721434266521000370270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.nio.charset.Charset; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.net.WWWFormCodec; /** * An entity composed of a list of url-encoded pairs. * This is typically useful while sending an HTTP POST request. * * @since 4.0 */ public class UrlEncodedFormEntity extends StringEntity { /** * Constructs a new {@link UrlEncodedFormEntity} with the list * of parameters in the specified encoding. * * @param parameters iterable collection of name/value pairs * @param charset encoding the name/value pairs be encoded with * * @since 4.2 */ public UrlEncodedFormEntity( final Iterable parameters, final Charset charset) { super(WWWFormCodec.format( parameters, charset != null ? charset : ContentType.APPLICATION_FORM_URLENCODED.getCharset()), charset != null ? ContentType.APPLICATION_FORM_URLENCODED.withCharset(charset) : ContentType.APPLICATION_FORM_URLENCODED); } /** * Constructs a new {@link UrlEncodedFormEntity} with the list * of parameters with the default encoding of {@link ContentType#APPLICATION_FORM_URLENCODED} * * @param parameters list of name/value pairs */ public UrlEncodedFormEntity (final List parameters){ this(parameters, null); } /** * Constructs a new {@link UrlEncodedFormEntity} with the list * of parameters with the default encoding of {@link ContentType#APPLICATION_FORM_URLENCODED} * * @param parameters iterable collection of name/value pairs * * @since 4.2 */ public UrlEncodedFormEntity ( final Iterable parameters) { this(parameters, null); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/000077500000000000000000000000001434266521000331175ustar00rootroot00000000000000AbstractContentBody.java000066400000000000000000000051021434266521000376150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.nio.charset.Charset; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * * @since 4.0 */ public abstract class AbstractContentBody implements ContentBody { private final ContentType contentType; /** * @since 4.3 */ public AbstractContentBody(final ContentType contentType) { super(); Args.notNull(contentType, "Content type"); this.contentType = contentType; } /** * @since 4.3 */ public ContentType getContentType() { return this.contentType; } @Override public String getMimeType() { return this.contentType.getMimeType(); } @Override public String getMediaType() { final String mimeType = this.contentType.getMimeType(); final int i = mimeType.indexOf('/'); if (i != -1) { return mimeType.substring(0, i); } return mimeType; } @Override public String getSubType() { final String mimeType = this.contentType.getMimeType(); final int i = mimeType.indexOf('/'); if (i != -1) { return mimeType.substring(i + 1); } return null; } @Override public String getCharset() { final Charset charset = this.contentType.getCharset(); return charset != null ? charset.name() : null; } } AbstractMultipartFormat.java000066400000000000000000000174141434266521000405300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.ByteArrayBuffer; /** * HttpMultipart represents a collection of MIME multipart encoded content bodies. * * @since 4.3 */ abstract class AbstractMultipartFormat { static ByteArrayBuffer encode( final Charset charset, final CharSequence string) { final ByteBuffer encoded = charset.encode(CharBuffer.wrap(string)); final ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining()); bab.append(encoded.array(), encoded.arrayOffset() + encoded.position(), encoded.remaining()); return bab; } static void writeBytes( final ByteArrayBuffer b, final OutputStream out) throws IOException { out.write(b.array(), 0, b.length()); } static void writeBytes( final CharSequence s, final Charset charset, final OutputStream out) throws IOException { final ByteArrayBuffer b = encode(charset, s); writeBytes(b, out); } static void writeBytes( final CharSequence s, final OutputStream out) throws IOException { final ByteArrayBuffer b = encode(StandardCharsets.ISO_8859_1, s); writeBytes(b, out); } static boolean isLineBreak(final char ch) { return ch == '\r' || ch == '\n' || ch == '\f' || ch == 11; } static CharSequence stripLineBreaks(final CharSequence s) { if (s == null) { return null; } boolean requiresRewrite = false; int n = 0; for (; n < s.length(); n++) { final char ch = s.charAt(n); if (isLineBreak(ch)) { requiresRewrite = true; break; } } if (!requiresRewrite) { return s; } final StringBuilder buf = new StringBuilder(); buf.append(s, 0, n); for (; n < s.length(); n++) { final char ch = s.charAt(n); if (isLineBreak(ch)) { buf.append(' '); } else { buf.append(ch); } } return buf.toString(); } static void writeField( final MimeField field, final OutputStream out) throws IOException { writeBytes(stripLineBreaks(field.getName()), out); writeBytes(FIELD_SEP, out); writeBytes(stripLineBreaks(field.getBody()), out); writeBytes(CR_LF, out); } static void writeField( final MimeField field, final Charset charset, final OutputStream out) throws IOException { writeBytes(stripLineBreaks(field.getName()), charset, out); writeBytes(FIELD_SEP, out); writeBytes(stripLineBreaks(field.getBody()), charset, out); writeBytes(CR_LF, out); } static final ByteArrayBuffer FIELD_SEP = encode(StandardCharsets.ISO_8859_1, ": "); static final ByteArrayBuffer CR_LF = encode(StandardCharsets.ISO_8859_1, "\r\n"); static final ByteArrayBuffer TWO_HYPHENS = encode(StandardCharsets.ISO_8859_1, "--"); final Charset charset; final String boundary; /** * Creates an instance with the specified settings. * * @param charset the character set to use. May be {@code null}, in which case {@link StandardCharsets#ISO_8859_1} is used. * @param boundary to use - must not be {@code null} * @throws IllegalArgumentException if charset is null or boundary is null */ public AbstractMultipartFormat(final Charset charset, final String boundary) { super(); Args.notNull(boundary, "Multipart boundary"); this.charset = charset != null ? charset : StandardCharsets.ISO_8859_1; this.boundary = boundary; } public AbstractMultipartFormat(final String boundary) { this(null, boundary); } public abstract List getParts(); void doWriteTo( final OutputStream out, final boolean writeContent) throws IOException { final ByteArrayBuffer boundaryEncoded = encode(this.charset, this.boundary); for (final MultipartPart part: getParts()) { writeBytes(TWO_HYPHENS, out); writeBytes(boundaryEncoded, out); writeBytes(CR_LF, out); formatMultipartHeader(part, out); writeBytes(CR_LF, out); if (writeContent) { part.getBody().writeTo(out); } writeBytes(CR_LF, out); } writeBytes(TWO_HYPHENS, out); writeBytes(boundaryEncoded, out); writeBytes(TWO_HYPHENS, out); writeBytes(CR_LF, out); } /** * Write the multipart header fields; depends on the style. */ protected abstract void formatMultipartHeader( final MultipartPart part, final OutputStream out) throws IOException; /** * Writes out the content in the multipart/form encoding. This method * produces slightly different formatting depending on its compatibility * mode. */ public void writeTo(final OutputStream out) throws IOException { doWriteTo(out, true); } /** * Determines the total length of the multipart content (content length of * individual parts plus that of extra elements required to delimit the parts * from one another). If any of the @{link BodyPart}s contained in this object * is of a streaming entity of unknown length the total length is also unknown. *

* This method buffers only a small amount of data in order to determine the * total length of the entire entity. The content of individual parts is not * buffered. *

* * @return total length of the multipart entity if known, {@code -1} * otherwise. */ public long getTotalLength() { long contentLen = 0; for (final MultipartPart part: getParts()) { final ContentBody body = part.getBody(); final long len = body.getContentLength(); if (len >= 0) { contentLen += len; } else { return -1; } } final ByteArrayOutputStream out = new ByteArrayOutputStream(); try { doWriteTo(out, false); final byte[] extra = out.toByteArray(); return contentLen + extra.length; } catch (final IOException ex) { // Should never happen return -1; } } } ByteArrayBody.java000066400000000000000000000057521434266521000364340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * Binary body part backed by a byte array. * * @see org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder * * @since 4.1 */ public class ByteArrayBody extends AbstractContentBody { /** * The contents of the file contained in this part. */ private final byte[] data; /** * The name of the file contained in this part. */ private final String filename; /** * @since 4.3 */ public ByteArrayBody(final byte[] data, final ContentType contentType, final String filename) { super(contentType); this.data = Args.notNull(data, "data"); this.filename = filename; } /** * Public constructor that creates a new ByteArrayBody. * * @param contentType the {@link ContentType} * @param data the array of byte. * * @since 5.2 */ public ByteArrayBody(final byte[] data, final ContentType contentType) { this(data, contentType, null); } /** * Creates a new ByteArrayBody. * * @param data The contents of the file contained in this part. * @param filename The name of the file contained in this part. */ public ByteArrayBody(final byte[] data, final String filename) { this(data, ContentType.APPLICATION_OCTET_STREAM, filename); } @Override public String getFilename() { return filename; } @Override public void writeTo(final OutputStream out) throws IOException { out.write(data); } @Override public String getCharset() { return null; } @Override public long getContentLength() { return data.length; } } ContentBody.java000066400000000000000000000026641434266521000361430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.IOException; import java.io.OutputStream; /** * * @since 4.0 */ public interface ContentBody extends ContentDescriptor { String getFilename(); void writeTo(OutputStream out) throws IOException; } ContentDescriptor.java000066400000000000000000000056451434266521000373660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; /** * Represents common content properties. */ public interface ContentDescriptor { /** * Returns the body descriptors MIME type. * @see #getMediaType() * @see #getSubType() * @return The MIME type, which has been parsed from the * content-type definition. Must not be null, but * "text/plain", if no content-type was specified. */ String getMimeType(); /** * Gets the defaulted MIME media type for this content. * For example {@code TEXT}, {@code IMAGE}, {@code MULTIPART} * @see #getMimeType() * @return the MIME media type when content-type specified, * otherwise the correct default ({@code TEXT}) */ String getMediaType(); /** * Gets the defaulted MIME sub type for this content. * @see #getMimeType() * @return the MIME media type when content-type is specified, * otherwise the correct default ({@code PLAIN}) */ String getSubType(); /** *

The body descriptors character set, defaulted appropriately for the MIME type.

*

* For {@code TEXT} types, this will be defaulted to {@code us-ascii}. * For other types, when the charset parameter is missing this property will be null. *

* @return Character set, which has been parsed from the * content-type definition. Not null for {@code TEXT} types, when unset will * be set to default {@code us-ascii}. For other types, when unset, * null will be returned. */ String getCharset(); /** * Returns the body descriptors content-length. * @return Content length, if known, or -1, to indicate the absence of a * content-length header. */ long getContentLength(); } FileBody.java000066400000000000000000000057741434266521000354150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * Binary body part backed by a file. * * @see org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder * * @since 4.0 */ public class FileBody extends AbstractContentBody { private final File file; private final String filename; public FileBody(final File file) { this(file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null); } /** * @since 4.3 */ public FileBody(final File file, final ContentType contentType, final String filename) { super(contentType); Args.notNull(file, "File"); this.file = file; this.filename = filename == null ? file.getName() : filename; } /** * @since 4.3 */ public FileBody(final File file, final ContentType contentType) { this(file, contentType, file != null ? file.getName() : null); } public InputStream getInputStream() throws IOException { return new FileInputStream(this.file); } @Override public void writeTo(final OutputStream out) throws IOException { Args.notNull(out, "Output stream"); try (InputStream in = new FileInputStream(this.file)) { final byte[] tmp = new byte[4096]; int l; while ((l = in.read(tmp)) != -1) { out.write(tmp, 0, l); } out.flush(); } } @Override public long getContentLength() { return this.file.length(); } @Override public String getFilename() { return filename; } public File getFile() { return this.file; } } FormBodyPart.java000066400000000000000000000040171434266521000362550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import org.apache.hc.core5.util.Args; /** * FormBodyPart class represents a content body that can be used as a part of multipart encoded * entities. This class automatically populates the header with standard fields based on * the content description of the enclosed body. * * @since 4.0 */ public class FormBodyPart extends MultipartPart { private final String name; FormBodyPart(final String name, final ContentBody body, final Header header) { super(body, header); Args.notNull(name, "Name"); Args.notNull(body, "Body"); this.name = name; } public String getName() { return this.name; } @Override public void addField(final String name, final String value) { Args.notNull(name, "Field name"); super.addField(name, value); } } FormBodyPartBuilder.java000066400000000000000000000122171434266521000375650ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * Builder for individual {@link org.apache.hc.client5.http.entity.mime.FormBodyPart}s. * * @since 4.4 */ public class FormBodyPartBuilder { private String name; private ContentBody body; private final Header header; public static FormBodyPartBuilder create(final String name, final ContentBody body) { return new FormBodyPartBuilder(name, body); } public static FormBodyPartBuilder create() { return new FormBodyPartBuilder(); } FormBodyPartBuilder(final String name, final ContentBody body) { this(); this.name = name; this.body = body; } FormBodyPartBuilder() { this.header = new Header(); } public FormBodyPartBuilder setName(final String name) { this.name = name; return this; } public FormBodyPartBuilder setBody(final ContentBody body) { this.body = body; return this; } /** * @since 4.6 */ public FormBodyPartBuilder addField(final String name, final String value, final List parameters) { Args.notNull(name, "Field name"); this.header.addField(new MimeField(name, value, parameters)); return this; } public FormBodyPartBuilder addField(final String name, final String value) { Args.notNull(name, "Field name"); this.header.addField(new MimeField(name, value)); return this; } public FormBodyPartBuilder setField(final String name, final String value) { Args.notNull(name, "Field name"); this.header.setField(new MimeField(name, value)); return this; } public FormBodyPartBuilder removeFields(final String name) { Args.notNull(name, "Field name"); this.header.removeFields(name); return this; } public FormBodyPart build() { Asserts.notBlank(this.name, "Name"); Asserts.notNull(this.body, "Content body"); final Header headerCopy = new Header(); final List fields = this.header.getFields(); for (final MimeField field: fields) { headerCopy.addField(field); } if (headerCopy.getField(MimeConsts.CONTENT_DISPOSITION) == null) { final List fieldParameters = new ArrayList<>(); fieldParameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_NAME, this.name)); if (this.body.getFilename() != null) { fieldParameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_FILENAME, this.body.getFilename())); } headerCopy.addField(new MimeField(MimeConsts.CONTENT_DISPOSITION, "form-data", fieldParameters)); } if (headerCopy.getField(MimeConsts.CONTENT_TYPE) == null) { final ContentType contentType; if (body instanceof AbstractContentBody) { contentType = ((AbstractContentBody) body).getContentType(); } else { contentType = null; } if (contentType != null) { headerCopy.addField(new MimeField(MimeConsts.CONTENT_TYPE, contentType.toString())); } else { final StringBuilder buffer = new StringBuilder(); buffer.append(this.body.getMimeType()); // MimeType cannot be null if (this.body.getCharset() != null) { // charset may legitimately be null buffer.append("; charset="); buffer.append(this.body.getCharset()); } headerCopy.addField(new MimeField(MimeConsts.CONTENT_TYPE, buffer.toString())); } } return new FormBodyPart(this.name, this.body, headerCopy); } } Header.java000066400000000000000000000104771434266521000351040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; /** * The header of a MIME entity. */ public class Header implements Iterable { private final List fields; private final Map> fieldMap; public Header() { super(); this.fields = new LinkedList<>(); this.fieldMap = new HashMap<>(); } public void addField(final MimeField field) { if (field == null) { return; } final String key = field.getName().toLowerCase(Locale.ROOT); final List values = this.fieldMap.computeIfAbsent(key, k -> new LinkedList<>()); values.add(field); this.fields.add(field); } public List getFields() { return new ArrayList<>(this.fields); } public MimeField getField(final String name) { if (name == null) { return null; } final String key = name.toLowerCase(Locale.ROOT); final List list = this.fieldMap.get(key); if (list != null && !list.isEmpty()) { return list.get(0); } return null; } public List getFields(final String name) { if (name == null) { return null; } final String key = name.toLowerCase(Locale.ROOT); final List list = this.fieldMap.get(key); if (list == null || list.isEmpty()) { return Collections.emptyList(); } return new ArrayList<>(list); } public int removeFields(final String name) { if (name == null) { return 0; } final String key = name.toLowerCase(Locale.ROOT); final List removed = fieldMap.remove(key); if (removed == null || removed.isEmpty()) { return 0; } this.fields.removeAll(removed); return removed.size(); } public void setField(final MimeField field) { if (field == null) { return; } final String key = field.getName().toLowerCase(Locale.ROOT); final List list = fieldMap.get(key); if (list == null || list.isEmpty()) { addField(field); return; } list.clear(); list.add(field); int firstOccurrence = -1; int index = 0; for (final Iterator it = this.fields.iterator(); it.hasNext(); index++) { final MimeField f = it.next(); if (f.getName().equalsIgnoreCase(field.getName())) { it.remove(); if (firstOccurrence == -1) { firstOccurrence = index; } } } this.fields.add(firstOccurrence, field); } @Override public Iterator iterator() { return Collections.unmodifiableList(fields).iterator(); } @Override public String toString() { return this.fields.toString(); } } HttpMultipartMode.java000066400000000000000000000035631434266521000373400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; /** * MIME compliance mode. * * @since 4.0 */ public enum HttpMultipartMode { /** * Legacy compatibility mode. *

* In this mode only the most essential fields are generated * such as Content-Type and Content-Disposition. */ LEGACY, /** * Strict MIME specification conformance. *

* Presently conforms to RFC 822, RFC 2045, RFC 2046. */ STRICT, /** * Extended MIME specification conformance. *

* In this mode header field values may contain international UTF-8 encoded * characters. *

* Presently conforms to RFC 6532 and RFC 7578. */ EXTENDED, } HttpRFC6532Multipart.java000066400000000000000000000042201434266521000373750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; class HttpRFC6532Multipart extends AbstractMultipartFormat { private final List parts; public HttpRFC6532Multipart( final Charset charset, final String boundary, final List parts) { super(charset, boundary); this.parts = parts; } @Override public List getParts() { return this.parts; } @Override protected void formatMultipartHeader( final MultipartPart part, final OutputStream out) throws IOException { // For RFC6532, we output all fields with UTF-8 encoding. final Header header = part.getHeader(); for (final MimeField field: header) { writeField(field, StandardCharsets.UTF_8, out); } } } HttpRFC7578Multipart.java000066400000000000000000000147741434266521000374270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; import java.util.BitSet; import java.util.List; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.ByteArrayBuffer; class HttpRFC7578Multipart extends AbstractMultipartFormat { private static final PercentCodec PERCENT_CODEC = new PercentCodec(); private final List parts; public HttpRFC7578Multipart( final Charset charset, final String boundary, final List parts) { super(charset, boundary); this.parts = parts; } @Override public List getParts() { return parts; } @Override protected void formatMultipartHeader(final MultipartPart part, final OutputStream out) throws IOException { for (final MimeField field: part.getHeader()) { if (MimeConsts.CONTENT_DISPOSITION.equalsIgnoreCase(field.getName())) { writeBytes(field.getName(), charset, out); writeBytes(FIELD_SEP, out); writeBytes(field.getValue(), out); final List parameters = field.getParameters(); for (int i = 0; i < parameters.size(); i++) { final NameValuePair parameter = parameters.get(i); final String name = parameter.getName(); final String value = parameter.getValue(); writeBytes("; ", out); writeBytes(name, out); writeBytes("=\"", out); if (value != null) { if (name.equalsIgnoreCase(MimeConsts.FIELD_PARAM_FILENAME)) { out.write(PERCENT_CODEC.encode(value.getBytes(charset))); } else { writeBytes(value, out); } } writeBytes("\"", out); } writeBytes(CR_LF, out); } else { writeField(field, charset, out); } } } static class PercentCodec { private static final byte ESCAPE_CHAR = '%'; private static final BitSet ALWAYSENCODECHARS = new BitSet(); static { ALWAYSENCODECHARS.set(' '); ALWAYSENCODECHARS.set('%'); } /** * Percent-Encoding implementation based on RFC 3986 */ public byte[] encode(final byte[] bytes) { if (bytes == null) { return null; } final CharsetEncoder characterSetEncoder = StandardCharsets.US_ASCII.newEncoder(); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); for (final byte c : bytes) { int b = c; if (b < 0) { b = 256 + b; } if (characterSetEncoder.canEncode((char) b) && !ALWAYSENCODECHARS.get(c)) { buffer.write(b); } else { buffer.write(ESCAPE_CHAR); final char hex1 = hexDigit(b >> 4); final char hex2 = hexDigit(b); buffer.write(hex1); buffer.write(hex2); } } return buffer.toByteArray(); } public byte[] decode(final byte[] bytes) { if (bytes == null) { return null; } final ByteArrayBuffer buffer = new ByteArrayBuffer(bytes.length); for (int i = 0; i < bytes.length; i++) { final int b = bytes[i]; if (b == ESCAPE_CHAR) { if (i >= bytes.length - 2) { throw new IllegalArgumentException("Invalid encoding: too short"); } final int u = digit16(bytes[++i]); final int l = digit16(bytes[++i]); buffer.append((char) ((u << 4) + l)); } else { buffer.append(b); } } return buffer.toByteArray(); } } /** * Radix used in encoding and decoding. */ private static final int RADIX = 16; /** * Returns the numeric value of the character {@code b} in radix 16. * * @param b * The byte to be converted. * @return The numeric value represented by the character in radix 16. * * @throws IllegalArgumentException * Thrown when the byte is not valid per {@link Character#digit(char,int)} */ static int digit16(final byte b) { final int i = Character.digit((char) b, RADIX); if (i == -1) { throw new IllegalArgumentException("Invalid encoding: not a valid digit (radix " + RADIX + "): " + b); } return i; } /** * Returns the upper case hex digit of the lower 4 bits of the int. * * @param b the input int * @return the upper case hex digit of the lower 4 bits of the int. */ static char hexDigit(final int b) { return Character.toUpperCase(Character.forDigit(b & 0xF, RADIX)); } } HttpStrictMultipart.java000066400000000000000000000041231434266521000377150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.List; class HttpStrictMultipart extends AbstractMultipartFormat { private final List parts; public HttpStrictMultipart( final Charset charset, final String boundary, final List parts) { super(charset, boundary); this.parts = parts; } @Override public List getParts() { return this.parts; } @Override protected void formatMultipartHeader( final MultipartPart part, final OutputStream out) throws IOException { // For strict, we output all fields with MIME-standard encoding. final Header header = part.getHeader(); for (final MimeField field: header) { writeField(field, out); } } } InputStreamBody.java000066400000000000000000000062341434266521000370010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * Binary body part backed by an input stream. * * @see org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder * * @since 4.0 */ public class InputStreamBody extends AbstractContentBody { private final InputStream in; private final String filename; private final long contentLength; public InputStreamBody(final InputStream in, final String filename) { this(in, ContentType.DEFAULT_BINARY, filename); } /** * @since 4.3 */ public InputStreamBody(final InputStream in, final ContentType contentType, final String filename) { this(in, contentType, filename, -1); } /** * @since 4.6 */ public InputStreamBody(final InputStream in, final ContentType contentType, final String filename, final long contentLength) { super(contentType); Args.notNull(in, "Input stream"); this.in = in; this.filename = filename; this.contentLength = contentLength >= 0 ? contentLength : -1; } /** * @since 4.3 */ public InputStreamBody(final InputStream in, final ContentType contentType) { this(in, contentType, null); } public InputStream getInputStream() { return this.in; } @Override public void writeTo(final OutputStream out) throws IOException { Args.notNull(out, "Output stream"); try { final byte[] tmp = new byte[4096]; int l; while ((l = this.in.read(tmp)) != -1) { out.write(tmp, 0, l); } out.flush(); } finally { this.in.close(); } } @Override public long getContentLength() { return this.contentLength; } @Override public String getFilename() { return this.filename; } } LegacyMultipart.java000066400000000000000000000052501434266521000370130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.List; /** * HttpBrowserCompatibleMultipart represents a collection of MIME multipart encoded * content bodies. This class is emulates browser compatibility, e.g. IE 5 or earlier. * * @since 4.3 */ class LegacyMultipart extends AbstractMultipartFormat { private final List parts; public LegacyMultipart( final Charset charset, final String boundary, final List parts) { super(charset, boundary); this.parts = parts; } @Override public List getParts() { return this.parts; } /** * Write the multipart header fields; depends on the style. */ @Override protected void formatMultipartHeader( final MultipartPart part, final OutputStream out) throws IOException { // For browser-compatible, only write Content-Disposition // Use content charset final Header header = part.getHeader(); final MimeField cd = header.getField(MimeConsts.CONTENT_DISPOSITION); if (cd != null) { writeField(cd, this.charset, out); } final String filename = part.getBody().getFilename(); if (filename != null) { final MimeField ct = header.getField(MimeConsts.CONTENT_TYPE); writeField(ct, this.charset, out); } } } MimeConsts.java000066400000000000000000000030071434266521000357640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; final class MimeConsts { public static final String CONTENT_TYPE = "Content-Type"; public static final String CONTENT_DISPOSITION = "Content-Disposition"; public static final String FIELD_PARAM_NAME = "name"; public static final String FIELD_PARAM_FILENAME = "filename"; } MimeField.java000066400000000000000000000065161434266521000355460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.hc.core5.http.NameValuePair; /** * Minimal MIME field. * * @since 4.0 */ public class MimeField { private final String name; private final String value; private final List parameters; public MimeField(final String name, final String value) { super(); this.name = name; this.value = value; this.parameters = Collections.emptyList(); } /** * @since 4.6 */ public MimeField(final String name, final String value, final List parameters) { this.name = name; this.value = value; this.parameters = parameters != null ? Collections.unmodifiableList(new ArrayList<>(parameters)) : Collections.emptyList(); } public MimeField(final MimeField from) { this(from.name, from.value, from.parameters); } public String getName() { return this.name; } /** * @since 4.6 */ public String getValue() { return this.value; } public String getBody() { final StringBuilder sb = new StringBuilder(); sb.append(this.value); for (int i = 0; i < this.parameters.size(); i++) { final NameValuePair parameter = this.parameters.get(i); sb.append("; "); sb.append(parameter.getName()); sb.append("=\""); final String v = parameter.getValue(); for (int n = 0; n < v.length(); n++) { final char ch = v.charAt(n); if (ch == '"' || ch == '\\' ) { sb.append("\\"); } sb.append(ch); } sb.append("\""); } return sb.toString(); } public List getParameters() { return this.parameters; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append(this.name); buffer.append(": "); buffer.append(this.getBody()); return buffer.toString(); } } MultipartEntityBuilder.java000066400000000000000000000230511434266521000403710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.File; import java.io.InputStream; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.util.Args; /** * Builder for multipart {@link HttpEntity}s. * * @since 5.0 */ public class MultipartEntityBuilder { /** * The pool of ASCII chars to be used for generating a multipart boundary. */ private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" .toCharArray(); private ContentType contentType; private HttpMultipartMode mode = HttpMultipartMode.STRICT; private String boundary; private Charset charset; private List multipartParts; /** * An empty immutable {@code NameValuePair} array. */ private static final NameValuePair[] EMPTY_NAME_VALUE_ARRAY = {}; public static MultipartEntityBuilder create() { return new MultipartEntityBuilder(); } MultipartEntityBuilder() { } public MultipartEntityBuilder setMode(final HttpMultipartMode mode) { this.mode = mode; return this; } public MultipartEntityBuilder setLaxMode() { this.mode = HttpMultipartMode.LEGACY; return this; } public MultipartEntityBuilder setStrictMode() { this.mode = HttpMultipartMode.STRICT; return this; } public MultipartEntityBuilder setBoundary(final String boundary) { this.boundary = boundary; return this; } /** * @since 4.4 */ public MultipartEntityBuilder setMimeSubtype(final String subType) { Args.notBlank(subType, "MIME subtype"); this.contentType = ContentType.create("multipart/" + subType); return this; } /** * @since 4.5 */ public MultipartEntityBuilder setContentType(final ContentType contentType) { Args.notNull(contentType, "Content type"); this.contentType = contentType; return this; } /** * Add parameter to the current {@link ContentType}. * * @param parameter The name-value pair parameter to add to the {@link ContentType}. * @return the {@link MultipartEntityBuilder} instance. * @since 5.2 */ public MultipartEntityBuilder addParameter(final BasicNameValuePair parameter) { this.contentType = contentType.withParameters(parameter); return this; } public MultipartEntityBuilder setCharset(final Charset charset) { this.charset = charset; return this; } /** * @since 4.4 */ public MultipartEntityBuilder addPart(final MultipartPart multipartPart) { if (multipartPart == null) { return this; } if (this.multipartParts == null) { this.multipartParts = new ArrayList<>(); } this.multipartParts.add(multipartPart); return this; } public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) { Args.notNull(name, "Name"); Args.notNull(contentBody, "Content body"); return addPart(FormBodyPartBuilder.create(name, contentBody).build()); } public MultipartEntityBuilder addTextBody( final String name, final String text, final ContentType contentType) { return addPart(name, new StringBody(text, contentType)); } public MultipartEntityBuilder addTextBody( final String name, final String text) { return addTextBody(name, text, ContentType.DEFAULT_TEXT); } public MultipartEntityBuilder addBinaryBody( final String name, final byte[] b, final ContentType contentType, final String filename) { return addPart(name, new ByteArrayBody(b, contentType, filename)); } public MultipartEntityBuilder addBinaryBody( final String name, final byte[] b) { return addPart(name, new ByteArrayBody(b, ContentType.DEFAULT_BINARY)); } public MultipartEntityBuilder addBinaryBody( final String name, final File file, final ContentType contentType, final String filename) { return addPart(name, new FileBody(file, contentType, filename)); } public MultipartEntityBuilder addBinaryBody( final String name, final File file) { return addBinaryBody(name, file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null); } public MultipartEntityBuilder addBinaryBody( final String name, final InputStream stream, final ContentType contentType, final String filename) { return addPart(name, new InputStreamBody(stream, contentType, filename)); } public MultipartEntityBuilder addBinaryBody(final String name, final InputStream stream) { return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null); } private String generateBoundary() { final ThreadLocalRandom rand = ThreadLocalRandom.current(); final int count = rand.nextInt(30, 41); // a random size from 30 to 40 final CharBuffer buffer = CharBuffer.allocate(count); while (buffer.hasRemaining()) { buffer.put(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]); } buffer.flip(); return buffer.toString(); } MultipartFormEntity buildEntity() { String boundaryCopy = boundary; if (boundaryCopy == null && contentType != null) { boundaryCopy = contentType.getParameter("boundary"); } if (boundaryCopy == null) { boundaryCopy = generateBoundary(); } Charset charsetCopy = charset; if (charsetCopy == null && contentType != null) { charsetCopy = contentType.getCharset(); } final List paramsList = new ArrayList<>(2); paramsList.add(new BasicNameValuePair("boundary", boundaryCopy)); if (charsetCopy != null) { paramsList.add(new BasicNameValuePair("charset", charsetCopy.name())); } final NameValuePair[] params = paramsList.toArray(EMPTY_NAME_VALUE_ARRAY); final ContentType contentTypeCopy; if (contentType != null) { contentTypeCopy = contentType.withParameters(params); } else { boolean formData = false; if (multipartParts != null) { for (final MultipartPart multipartPart : multipartParts) { if (multipartPart instanceof FormBodyPart) { formData = true; break; } } } if (formData) { contentTypeCopy = ContentType.MULTIPART_FORM_DATA.withParameters(params); } else { contentTypeCopy = ContentType.create("multipart/mixed", params); } } final List multipartPartsCopy = multipartParts != null ? new ArrayList<>(multipartParts) : Collections.emptyList(); final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT; final AbstractMultipartFormat form; switch (modeCopy) { case LEGACY: form = new LegacyMultipart(charsetCopy, boundaryCopy, multipartPartsCopy); break; case EXTENDED: if (contentTypeCopy.isSameMimeType(ContentType.MULTIPART_FORM_DATA)) { if (charsetCopy == null) { charsetCopy = StandardCharsets.UTF_8; } form = new HttpRFC7578Multipart(charsetCopy, boundaryCopy, multipartPartsCopy); } else { form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, multipartPartsCopy); } break; default: form = new HttpStrictMultipart(StandardCharsets.US_ASCII, boundaryCopy, multipartPartsCopy); } return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength()); } public HttpEntity build() { return buildEntity(); } } MultipartFormEntity.java000066400000000000000000000073021434266521000377070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Set; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ContentTooLongException; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; class MultipartFormEntity implements HttpEntity { private final AbstractMultipartFormat multipart; private final ContentType contentType; private final long contentLength; MultipartFormEntity( final AbstractMultipartFormat multipart, final ContentType contentType, final long contentLength) { super(); this.multipart = multipart; this.contentType = contentType; this.contentLength = contentLength; } AbstractMultipartFormat getMultipart() { return this.multipart; } @Override public boolean isRepeatable() { return this.contentLength != -1; } @Override public boolean isChunked() { return !isRepeatable(); } @Override public boolean isStreaming() { return !isRepeatable(); } @Override public long getContentLength() { return this.contentLength; } @Override public String getContentType() { return this.contentType != null ? this.contentType.toString() : null; } @Override public String getContentEncoding() { return null; } @Override public InputStream getContent() throws IOException { if (this.contentLength < 0) { throw new ContentTooLongException("Content length is unknown"); } else if (this.contentLength > 25 * 1024) { throw new ContentTooLongException("Content length is too long: " + this.contentLength); } final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); writeTo(outStream); outStream.flush(); return new ByteArrayInputStream(outStream.toByteArray()); } @Override public void writeTo(final OutputStream outStream) throws IOException { this.multipart.writeTo(outStream); } @Override public Supplier> getTrailers() { return null; } @Override public Set getTrailerNames() { return null; } @Override public void close() throws IOException { } } MultipartPart.java000066400000000000000000000040721434266521000365160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; /** * MultipartPart class represents a content body that can be used as a part of multipart encoded * entities. This class automatically populates the header with standard fields based on * the content description of the enclosed body. * * @since 5.0 */ public class MultipartPart { private final Header header; private final ContentBody body; MultipartPart(final ContentBody body, final Header header) { super(); this.body = body; this.header = header != null ? header : new Header(); } public ContentBody getBody() { return this.body; } public Header getHeader() { return this.header; } void addField(final String name, final String value) { addField(new MimeField(name, value)); } void addField(final MimeField field) { this.header.addField(field); } } MultipartPartBuilder.java000066400000000000000000000103241434266521000400220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; /** * Builder for individual {@link MultipartPart}s. * * @since 4.4 */ public class MultipartPartBuilder { private ContentBody body; private final Header header; public static MultipartPartBuilder create(final ContentBody body) { return new MultipartPartBuilder(body); } public static MultipartPartBuilder create() { return new MultipartPartBuilder(); } MultipartPartBuilder(final ContentBody body) { this(); this.body = body; } MultipartPartBuilder() { this.header = new Header(); } public MultipartPartBuilder setBody(final ContentBody body) { this.body = body; return this; } public MultipartPartBuilder addHeader(final String name, final String value, final List parameters) { Args.notNull(name, "Header name"); this.header.addField(new MimeField(name, value, parameters)); return this; } public MultipartPartBuilder addHeader(final String name, final String value) { Args.notNull(name, "Header name"); this.header.addField(new MimeField(name, value)); return this; } public MultipartPartBuilder setHeader(final String name, final String value) { Args.notNull(name, "Header name"); this.header.setField(new MimeField(name, value)); return this; } public MultipartPartBuilder removeHeaders(final String name) { Args.notNull(name, "Header name"); this.header.removeFields(name); return this; } public MultipartPart build() { Asserts.notNull(this.body, "Content body"); final Header headerCopy = new Header(); final List fields = this.header.getFields(); for (final MimeField field: fields) { headerCopy.addField(field); } if (headerCopy.getField(MimeConsts.CONTENT_TYPE) == null) { final ContentType contentType; if (body instanceof AbstractContentBody) { contentType = ((AbstractContentBody) body).getContentType(); } else { contentType = null; } if (contentType != null) { headerCopy.addField(new MimeField(MimeConsts.CONTENT_TYPE, contentType.toString())); } else { final StringBuilder buffer = new StringBuilder(); buffer.append(this.body.getMimeType()); // MimeType cannot be null if (this.body.getCharset() != null) { // charset may legitimately be null buffer.append("; charset="); buffer.append(this.body.getCharset()); } headerCopy.addField(new MimeField(MimeConsts.CONTENT_TYPE, buffer.toString())); } } return new MultipartPart(this.body, headerCopy); } } StringBody.java000066400000000000000000000052301434266521000357670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Args; /** * Text body part backed by a byte array. * * @see org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder * * @since 4.0 */ public class StringBody extends AbstractContentBody { private final byte[] content; /** * @since 4.3 */ public StringBody(final String text, final ContentType contentType) { super(contentType); Args.notNull(text, "Text"); final Charset charset = contentType.getCharset(); this.content = text.getBytes(charset != null ? charset : StandardCharsets.US_ASCII); } public Reader getReader() { final Charset charset = getContentType().getCharset(); return new InputStreamReader( new ByteArrayInputStream(this.content), charset != null ? charset : StandardCharsets.US_ASCII); } @Override public void writeTo(final OutputStream out) throws IOException { Args.notNull(out, "Output stream"); out.write(this.content); } @Override public long getContentLength() { return this.content.length; } @Override public String getFilename() { return null; } } package-info.java000066400000000000000000000024041434266521000362270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * MIME coded HTTP entity implementations. */ package org.apache.hc.client5.http.entity.mime; package-info.java000066400000000000000000000024041434266521000353000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client specific HTTP entity implementations. */ package org.apache.hc.client5.http.entity; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/000077500000000000000000000000001434266521000316155ustar00rootroot00000000000000ChainElement.java000066400000000000000000000026141434266521000347400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; /** * Supported elements of request execution pipeline. * * @since 5.0 */ public enum ChainElement { REDIRECT, COMPRESS, BACK_OFF, RETRY, CACHING, PROTOCOL, CONNECT, MAIN_TRANSPORT } ConnPoolSupport.java000066400000000000000000000055121434266521000355300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.util.Identifiable; /** * Connection pooling support methods. * * @since 5.0 */ @Internal public final class ConnPoolSupport { public static String getId(final Object object) { if (object == null) { return null; } return object instanceof Identifiable ? ((Identifiable) object).getId() : object.getClass().getSimpleName() + "-" + Integer.toHexString(System.identityHashCode(object)); } public static String formatStats( final HttpRoute route, final Object state, final ConnPoolControl connPool) { final StringBuilder buf = new StringBuilder(); buf.append("[route: ").append(route).append("]"); if (state != null) { buf.append("[state: ").append(state).append("]"); } final PoolStats totals = connPool.getTotalStats(); final PoolStats stats = connPool.getStats(route); buf.append("[total available: ").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(); } } ConnectionShutdownException.java000066400000000000000000000032011434266521000401070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; /** * Signals that the connection has been shut down or released back to the * the connection pool * * @since 4.1 */ public class ConnectionShutdownException extends IllegalStateException { private static final long serialVersionUID = 5868657401162844497L; /** * Creates a new ConnectionShutdownException with a {@code null} detail message. */ public ConnectionShutdownException() { super(); } } CookieSpecSupport.java000066400000000000000000000066011434266521000360250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.impl.cookie.IgnoreCookieSpecFactory; import org.apache.hc.client5.http.impl.cookie.RFC6265CookieSpecFactory; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.apache.hc.client5.http.psl.PublicSuffixMatcherLoader; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; /** * Cookie support methods. * * @since 5.0 */ @Internal public final class CookieSpecSupport { /** * Creates a builder containing the default registry entries, using the provided public suffix matcher. */ public static RegistryBuilder createDefaultBuilder(final PublicSuffixMatcher publicSuffixMatcher) { return RegistryBuilder.create() .register(StandardCookieSpec.RELAXED, new RFC6265CookieSpecFactory( RFC6265CookieSpecFactory.CompatibilityLevel.RELAXED, publicSuffixMatcher)) .register(StandardCookieSpec.STRICT, new RFC6265CookieSpecFactory( RFC6265CookieSpecFactory.CompatibilityLevel.STRICT, publicSuffixMatcher)) .register(StandardCookieSpec.IGNORE, new IgnoreCookieSpecFactory()); } /** * Creates a builder containing the default registry entries with the default public suffix matcher. */ public static RegistryBuilder createDefaultBuilder() { return createDefaultBuilder(PublicSuffixMatcherLoader.getDefault()); } /** * Creates the default registry, using the default public suffix matcher. */ public static Lookup createDefault() { return createDefault(PublicSuffixMatcherLoader.getDefault()); } /** * Creates the default registry with the provided public suffix matcher */ public static Lookup createDefault(final PublicSuffixMatcher publicSuffixMatcher) { return createDefaultBuilder(publicSuffixMatcher).build(); } private CookieSpecSupport() {} } DefaultAuthenticationStrategy.java000066400000000000000000000121571434266521000404160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default implementation of {@link AuthenticationStrategy} * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultAuthenticationStrategy implements AuthenticationStrategy { private static final Logger LOG = LoggerFactory.getLogger(DefaultAuthenticationStrategy.class); public static final DefaultAuthenticationStrategy INSTANCE = new DefaultAuthenticationStrategy(); private static final List DEFAULT_SCHEME_PRIORITY = Collections.unmodifiableList(Arrays.asList( StandardAuthScheme.SPNEGO, StandardAuthScheme.KERBEROS, StandardAuthScheme.NTLM, StandardAuthScheme.DIGEST, StandardAuthScheme.BASIC)); @Override public List select( final ChallengeType challengeType, final Map challenges, final HttpContext context) { Args.notNull(challengeType, "ChallengeType"); Args.notNull(challenges, "Map of auth challenges"); Args.notNull(context, "HTTP context"); final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); final List options = new ArrayList<>(); final Lookup registry = clientContext.getAuthSchemeRegistry(); if (registry == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Auth scheme registry not set in the context", exchangeId); } return options; } final RequestConfig config = clientContext.getRequestConfig(); Collection authPrefs = challengeType == ChallengeType.TARGET ? config.getTargetPreferredAuthSchemes() : config.getProxyPreferredAuthSchemes(); if (authPrefs == null) { authPrefs = DEFAULT_SCHEME_PRIORITY; } if (LOG.isDebugEnabled()) { LOG.debug("{} Authentication schemes in the order of preference: {}", exchangeId, authPrefs); } for (final String schemeName: authPrefs) { final AuthChallenge challenge = challenges.get(schemeName.toLowerCase(Locale.ROOT)); if (challenge != null) { final AuthSchemeFactory authSchemeFactory = registry.lookup(schemeName); if (authSchemeFactory == null) { if (LOG.isWarnEnabled()) { LOG.warn("{} Authentication scheme {} not supported", exchangeId, schemeName); // Try again } continue; } final AuthScheme authScheme = authSchemeFactory.create(context); options.add(authScheme); } else { if (LOG.isDebugEnabled()) { LOG.debug("{} Challenge for {} authentication scheme not available", exchangeId, schemeName); } } } return options; } } DefaultClientConnectionReuseStrategy.java000066400000000000000000000045331434266521000417000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy; import org.apache.hc.core5.http.protocol.HttpContext; /** * Extension of core {@link DefaultConnectionReuseStrategy} that treats * CONNECT method exchanges involved in proxy tunnelling as a special case. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultClientConnectionReuseStrategy extends DefaultConnectionReuseStrategy { public static final DefaultClientConnectionReuseStrategy INSTANCE = new DefaultClientConnectionReuseStrategy(); @Override public boolean keepAlive(final HttpRequest request, final HttpResponse response, final HttpContext context) { if (Method.CONNECT.isSame(request.getMethod()) && response.getCode() == HttpStatus.SC_OK) { return true; } return super.keepAlive(request, response, context); } } DefaultConnectionKeepAliveStrategy.java000066400000000000000000000063251434266521000413240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.util.Iterator; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** * Default implementation of a strategy deciding duration * that a connection can remain idle. *

* The default implementation looks solely at the 'Keep-Alive' * header's timeout token. *

* * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy { public static final DefaultConnectionKeepAliveStrategy INSTANCE = new DefaultConnectionKeepAliveStrategy(); @Override public TimeValue getKeepAliveDuration(final HttpResponse response, final HttpContext context) { Args.notNull(response, "HTTP response"); final Iterator it = MessageSupport.iterate(response, HeaderElements.KEEP_ALIVE); while (it.hasNext()) { final HeaderElement he = it.next(); final String param = he.getName(); final String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return TimeValue.ofSeconds(Long.parseLong(value)); } catch(final NumberFormatException ignore) { } } } final HttpClientContext clientContext = HttpClientContext.adapt(context); final RequestConfig requestConfig = clientContext.getRequestConfig(); return requestConfig.getConnectionKeepAlive(); } } DefaultHttpRequestRetryStrategy.java000066400000000000000000000203041434266521000407460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.io.IOException; import java.io.InterruptedIOException; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.net.UnknownHostException; import java.time.Instant; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; import javax.net.ssl.SSLException; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** * Default implementation of the {@link HttpRequestRetryStrategy} interface. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultHttpRequestRetryStrategy implements HttpRequestRetryStrategy { public static final DefaultHttpRequestRetryStrategy INSTANCE = new DefaultHttpRequestRetryStrategy(); /** * Maximum number of allowed retries */ private final int maxRetries; /** * Retry interval between subsequent retries */ private final TimeValue defaultRetryInterval; /** * Derived {@code IOExceptions} which shall not be retried */ private final Set> nonRetriableIOExceptionClasses; /** * HTTP status codes which shall be retried */ private final Set retriableCodes; protected DefaultHttpRequestRetryStrategy( final int maxRetries, final TimeValue defaultRetryInterval, final Collection> clazzes, final Collection codes) { Args.notNegative(maxRetries, "maxRetries"); Args.notNegative(defaultRetryInterval.getDuration(), "defaultRetryInterval"); this.maxRetries = maxRetries; this.defaultRetryInterval = defaultRetryInterval; this.nonRetriableIOExceptionClasses = new HashSet<>(clazzes); this.retriableCodes = new HashSet<>(codes); } /** * Create the HTTP request retry strategy using the following list of * non-retriable I/O exception classes:
*
    *
  • InterruptedIOException
  • *
  • UnknownHostException
  • *
  • ConnectException
  • *
  • ConnectionClosedException
  • *
  • NoRouteToHostException
  • *
  • SSLException
  • *
* * and retriable HTTP status codes:
*
    *
  • SC_TOO_MANY_REQUESTS (429)
  • *
  • SC_SERVICE_UNAVAILABLE (503)
  • *
* * @param maxRetries how many times to retry; 0 means no retries * @param defaultRetryInterval the default retry interval between * subsequent retries if the {@code Retry-After} header is not set * or invalid. */ public DefaultHttpRequestRetryStrategy( final int maxRetries, final TimeValue defaultRetryInterval) { this(maxRetries, defaultRetryInterval, Arrays.asList( InterruptedIOException.class, UnknownHostException.class, ConnectException.class, ConnectionClosedException.class, NoRouteToHostException.class, SSLException.class), Arrays.asList( HttpStatus.SC_TOO_MANY_REQUESTS, HttpStatus.SC_SERVICE_UNAVAILABLE)); } /** * Create the HTTP request retry strategy with a max retry count of 1, * default retry interval of 1 second, and using the following list of * non-retriable I/O exception classes:
*
    *
  • InterruptedIOException
  • *
  • UnknownHostException
  • *
  • ConnectException
  • *
  • ConnectionClosedException
  • *
  • SSLException
  • *
* * and retriable HTTP status codes:
*
    *
  • SC_TOO_MANY_REQUESTS (429)
  • *
  • SC_SERVICE_UNAVAILABLE (503)
  • *
*/ public DefaultHttpRequestRetryStrategy() { this(1, TimeValue.ofSeconds(1L)); } @Override public boolean retryRequest( final HttpRequest request, final IOException exception, final int execCount, final HttpContext context) { Args.notNull(request, "request"); Args.notNull(exception, "exception"); if (execCount > this.maxRetries) { // Do not retry if over max retries return false; } if (this.nonRetriableIOExceptionClasses.contains(exception.getClass())) { return false; } else { for (final Class rejectException : this.nonRetriableIOExceptionClasses) { if (rejectException.isInstance(exception)) { return false; } } } if (request instanceof CancellableDependency && ((CancellableDependency) request).isCancelled()) { return false; } // Retry if the request is considered idempotent return handleAsIdempotent(request); } @Override public boolean retryRequest( final HttpResponse response, final int execCount, final HttpContext context) { Args.notNull(response, "response"); return execCount <= this.maxRetries && retriableCodes.contains(response.getCode()); } @Override public TimeValue getRetryInterval( final HttpResponse response, final int execCount, final HttpContext context) { Args.notNull(response, "response"); final Header header = response.getFirstHeader(HttpHeaders.RETRY_AFTER); TimeValue retryAfter = null; if (header != null) { final String value = header.getValue(); try { retryAfter = TimeValue.ofSeconds(Long.parseLong(value)); } catch (final NumberFormatException ignore) { final Instant retryAfterDate = DateUtils.parseStandardDate(value); if (retryAfterDate != null) { retryAfter = TimeValue.ofMilliseconds(retryAfterDate.toEpochMilli() - System.currentTimeMillis()); } } if (TimeValue.isPositive(retryAfter)) { return retryAfter; } } return this.defaultRetryInterval; } protected boolean handleAsIdempotent(final HttpRequest request) { return Method.isIdempotent(request.getMethod()); } } DefaultRedirectStrategy.java000066400000000000000000000111741434266521000371760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.net.URI; import java.net.URISyntaxException; import java.util.Locale; import org.apache.hc.client5.http.protocol.RedirectStrategy; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.util.Args; /** * Default implementation of {@link RedirectStrategy}. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultRedirectStrategy implements RedirectStrategy { public static final DefaultRedirectStrategy INSTANCE = new DefaultRedirectStrategy(); @Override public boolean isRedirected( final HttpRequest request, final HttpResponse response, final HttpContext context) throws ProtocolException { Args.notNull(request, "HTTP request"); Args.notNull(response, "HTTP response"); if (!response.containsHeader(HttpHeaders.LOCATION)) { return false; } final int statusCode = response.getCode(); switch (statusCode) { case HttpStatus.SC_MOVED_PERMANENTLY: case HttpStatus.SC_MOVED_TEMPORARILY: case HttpStatus.SC_SEE_OTHER: case HttpStatus.SC_TEMPORARY_REDIRECT: case HttpStatus.SC_PERMANENT_REDIRECT: return true; default: return false; } } @Override public URI getLocationURI( final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException { Args.notNull(request, "HTTP request"); Args.notNull(response, "HTTP response"); Args.notNull(context, "HTTP context"); //get the location header to find out where to redirect to final Header locationHeader = response.getFirstHeader(HttpHeaders.LOCATION); if (locationHeader == null) { throw new HttpException("Redirect location is missing"); } final String location = locationHeader.getValue(); URI uri = createLocationURI(location); try { if (!uri.isAbsolute()) { // Resolve location URI uri = URIUtils.resolve(request.getUri(), uri); } } catch (final URISyntaxException ex) { throw new ProtocolException(ex.getMessage(), ex); } return uri; } /** * @since 4.1 */ protected URI createLocationURI(final String location) throws ProtocolException { try { final URIBuilder b = new URIBuilder(new URI(location).normalize()); final String host = b.getHost(); if (host != null) { b.setHost(host.toLowerCase(Locale.ROOT)); } if (b.isPathEmpty()) { b.setPathSegments(""); } return b.build(); } catch (final URISyntaxException ex) { throw new ProtocolException("Invalid redirect URI: " + location, ex); } } } DefaultSchemePortResolver.java000066400000000000000000000046031434266521000375040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Args; /** * Default {@link SchemePortResolver}. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultSchemePortResolver implements SchemePortResolver { public static final DefaultSchemePortResolver INSTANCE = new DefaultSchemePortResolver(); @Override public int resolve(final HttpHost host) { Args.notNull(host, "HTTP host"); return resolve(host.getSchemeName(), host); } @Override public int resolve(final String scheme, final NamedEndpoint endpoint) { Args.notNull(endpoint, "Endpoint"); final int port = endpoint.getPort(); if (port > 0) { return port; } if (URIScheme.HTTP.same(scheme)) { return 80; } else if (URIScheme.HTTPS.same(scheme)) { return 443; } else { return -1; } } } DefaultUserTokenHandler.java000066400000000000000000000104531434266521000371260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.security.Principal; import javax.net.ssl.SSLSession; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; /** * Default implementation of {@link UserTokenHandler}. This class will use * an instance of {@link Principal} as a state object for 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. *

* DefaultUserTokenHandler will use the user principal 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.STATELESS) public class DefaultUserTokenHandler implements UserTokenHandler { public static final DefaultUserTokenHandler INSTANCE = new DefaultUserTokenHandler(); @Override public Object getUserToken(final HttpRoute route, final HttpContext context) { return getUserToken(route, null, context); } @Override public Object getUserToken(final HttpRoute route, final HttpRequest request, final HttpContext context) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final HttpHost target = request != null ? new HttpHost(request.getScheme(), request.getAuthority()) : route.getTargetHost(); final AuthExchange targetAuthExchange = clientContext.getAuthExchange(target); if (targetAuthExchange != null) { final Principal authPrincipal = getAuthPrincipal(targetAuthExchange); if (authPrincipal != null) { return authPrincipal; } } final HttpHost proxy = route.getProxyHost(); if (proxy != null) { final AuthExchange proxyAuthExchange = clientContext.getAuthExchange(proxy); if (proxyAuthExchange != null) { final Principal authPrincipal = getAuthPrincipal(proxyAuthExchange); if (authPrincipal != null) { return authPrincipal; } } } final SSLSession sslSession = clientContext.getSSLSession(); if (sslSession != null) { return sslSession.getLocalPrincipal(); } return null; } private static Principal getAuthPrincipal(final AuthExchange authExchange) { final AuthScheme scheme = authExchange.getAuthScheme(); if (scheme != null && scheme.isConnectionBased()) { return scheme.getPrincipal(); } return null; } } ExecSupport.java000066400000000000000000000032231434266521000346620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.core5.annotation.Internal; /** * Request execution support methods. * * @since 5.0 */ @Internal public final class ExecSupport { private static final PrefixedIncrementingId INCREMENTING_ID = new PrefixedIncrementingId("ex-"); public static long getNextExecNumber() { return INCREMENTING_ID.getNextNumber(); } public static String getNextExchangeId() { return INCREMENTING_ID.getNextId(); } } IdleConnectionEvictor.java000066400000000000000000000073221434266521000366360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.util.concurrent.ThreadFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * This class maintains a background thread to enforce an eviction policy for expired / idle * persistent connections kept alive in the connection pool. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public final class IdleConnectionEvictor { private final ThreadFactory threadFactory; private final Thread thread; public IdleConnectionEvictor(final ConnPoolControl connectionManager, final ThreadFactory threadFactory, final TimeValue sleepTime, final TimeValue maxIdleTime) { Args.notNull(connectionManager, "Connection manager"); this.threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory("idle-connection-evictor", true); final TimeValue localSleepTime = sleepTime != null ? sleepTime : TimeValue.ofSeconds(5); this.thread = this.threadFactory.newThread(() -> { try { while (!Thread.currentThread().isInterrupted()) { localSleepTime.sleep(); connectionManager.closeExpired(); if (maxIdleTime != null) { connectionManager.closeIdle(maxIdleTime); } } } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } catch (final Exception ex) { } }); } public IdleConnectionEvictor(final ConnPoolControl connectionManager, final TimeValue sleepTime, final TimeValue maxIdleTime) { this(connectionManager, null, sleepTime, maxIdleTime); } public IdleConnectionEvictor(final ConnPoolControl connectionManager, final TimeValue maxIdleTime) { this(connectionManager, null, maxIdleTime, maxIdleTime); } public void start() { thread.start(); } public void shutdown() { thread.interrupt(); } public boolean isRunning() { return thread.isAlive(); } public void awaitTermination(final Timeout timeout) throws InterruptedException { thread.join(timeout != null ? timeout.toMilliseconds() : Long.MAX_VALUE); } } InMemoryDnsResolver.java000066400000000000000000000072711434266521000363360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * In-memory {@link DnsResolver} implementation. * * @since 4.2 */ @Contract(threading = ThreadingBehavior.STATELESS) public class InMemoryDnsResolver implements DnsResolver { /** Logger associated to this class. */ private static final Logger LOG = LoggerFactory.getLogger(InMemoryDnsResolver.class); /** * In-memory collection that will hold the associations between a host name * and an array of InetAddress instances. */ private final Map dnsMap; /** * Builds a DNS resolver that will resolve the host names against a * collection held in-memory. */ public InMemoryDnsResolver() { dnsMap = new ConcurrentHashMap<>(); } /** * Associates the given array of IP addresses to the given host in this DNS overrider. * The IP addresses are assumed to be already resolved. * * @param host * The host name to be associated with the given IP. * @param ips * array of IP addresses to be resolved by this DNS overrider to the given * host name. */ public void add(final String host, final InetAddress... ips) { Args.notNull(host, "Host name"); Args.notNull(ips, "Array of IP addresses"); dnsMap.put(host, ips); } /** * {@inheritDoc} */ @Override public InetAddress[] resolve(final String host) throws UnknownHostException { final InetAddress[] resolvedAddresses = dnsMap.get(host); if (LOG.isInfoEnabled()) { LOG.info("Resolving {} to {}", host, Arrays.deepToString(resolvedAddresses)); } if(resolvedAddresses == null){ throw new UnknownHostException(host + " cannot be resolved"); } return resolvedAddresses; } @Override public String resolveCanonicalHostname(final String host) throws UnknownHostException { final InetAddress[] resolvedAddresses = resolve(host); if (resolvedAddresses.length > 0) { return resolvedAddresses[0].getCanonicalHostName(); } return host; } } MessageCopier.java000066400000000000000000000026711434266521000351350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.core5.http.HttpMessage; /** * Abstract HTTP message copier. * * @since 5.0 * * @deprecated Use message builders. */ @Deprecated public interface MessageCopier { T copy(T object); } NoopUserTokenHandler.java000066400000000000000000000036011434266521000364520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * Noop implementation of {@link UserTokenHandler} that always returns {@code null}. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class NoopUserTokenHandler implements UserTokenHandler { public static final NoopUserTokenHandler INSTANCE = new NoopUserTokenHandler(); @Override public Object getUserToken(final HttpRoute route, final HttpContext context) { return null; } } Operations.java000066400000000000000000000067671434266521000345440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.hc.core5.concurrent.Cancellable; /** * Common cancellable operations. * * @since 5.0 */ public final class Operations { private final static Cancellable NOOP_CANCELLABLE = () -> false; /** * This class represents a {@link Future} in the completed state with a fixed result. * The outcome of the future cannot be altered and it cannot be cancelled. * * @param operation result representation. */ public static class CompletedFuture implements Future { private final T result; public CompletedFuture(final T result) { this.result = result; } @Override public T get() throws InterruptedException, ExecutionException { return result; } @Override public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return result; } @Override public boolean cancel(final boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return true; } } /** * Creates a {@link Cancellable} operation handle for an ongoing process * or operation that cannot be cancelled. Attempts to cancel the operation * with this handle will have no effect. * * @return the no-op cancellable operation handle. */ public static Cancellable nonCancellable() { return NOOP_CANCELLABLE; } /** * Creates a {@link Cancellable} operation handle for an ongoing process * or operation represented by a {@link Future}. * * @param future the result future * @return the cancellable operation handle. */ public static Cancellable cancellable(final Future future) { if (future == null) { return NOOP_CANCELLABLE; } if (future instanceof Cancellable) { return (Cancellable) future; } return () -> future.cancel(true); } } PrefixedIncrementingId.java000066400000000000000000000071101434266521000367660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.util.Args; /** * A thread safe incrementing identifier. * * @since 5.1.4 */ @Internal public final class PrefixedIncrementingId { private final AtomicLong count = new AtomicLong(0); private final String prefix0; private final String prefix1; private final String prefix2; private final String prefix3; private final String prefix4; private final String prefix5; private final String prefix6; private final String prefix7; private final String prefix8; private final String prefix9; /** * Creates an incrementing identifier. * @param prefix string prefix for generated IDs */ public PrefixedIncrementingId(final String prefix) { this.prefix0 = Args.notNull(prefix, "prefix"); this.prefix1 = prefix0 + '0'; this.prefix2 = prefix1 + '0'; this.prefix3 = prefix2 + '0'; this.prefix4 = prefix3 + '0'; this.prefix5 = prefix4 + '0'; this.prefix6 = prefix5 + '0'; this.prefix7 = prefix6 + '0'; this.prefix8 = prefix7 + '0'; this.prefix9 = prefix8 + '0'; } public long getNextNumber() { return count.incrementAndGet(); } public String getNextId() { return createId(count.incrementAndGet()); } /** * Create an ID from this instance's prefix and zero padded specified value. * * Hand rolled equivalent to `String.format("ex-%010d", value)` optimized to reduce * allocation and CPU overhead. */ String createId(final long value) { final String longString = Long.toString(value); switch (longString.length()) { case 1: return prefix9 + longString; case 2: return prefix8 + longString; case 3: return prefix7 + longString; case 4: return prefix6 + longString; case 5: return prefix5 + longString; case 6: return prefix4 + longString; case 7: return prefix3 + longString; case 8: return prefix2 + longString; case 9: return prefix1 + longString; default: return prefix0 + longString; } } } RequestCopier.java000066400000000000000000000043141434266521000351750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.util.Iterator; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.message.BasicHttpRequest; /** * {@link HttpRequest} copier. * * @since 5.0 * * @deprecated Use {@link org.apache.hc.core5.http.support.BasicRequestBuilder} */ @Deprecated public final class RequestCopier implements MessageCopier { public static final RequestCopier INSTANCE = new RequestCopier(); @Override public HttpRequest copy(final HttpRequest original) { if (original == null) { return null; } final BasicHttpRequest copy = new BasicHttpRequest(original.getMethod(), null, original.getPath()); copy.setScheme(original.getScheme()); copy.setAuthority(original.getAuthority()); copy.setVersion(original.getVersion()); for (final Iterator
it = original.headerIterator(); it.hasNext(); ) { copy.addHeader(it.next()); } return copy; } } RequestSupport.java000066400000000000000000000052021434266521000354250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.net.PercentCodec; import org.apache.hc.core5.net.URIBuilder; /** * Protocol support methods. For internal use only. * * @since 5.2 */ @Internal public final class RequestSupport { public static String extractPathPrefix(final HttpRequest request) { final String path = request.getPath(); try { final URIBuilder uriBuilder = new URIBuilder(path); uriBuilder.setFragment(null); uriBuilder.clearParameters(); uriBuilder.normalizeSyntax(); final List pathSegments = uriBuilder.getPathSegments(); if (!pathSegments.isEmpty()) { pathSegments.remove(pathSegments.size() - 1); } if (pathSegments.isEmpty()) { return "/"; } else { final StringBuilder buf = new StringBuilder(); buf.append('/'); for (final String pathSegment : pathSegments) { PercentCodec.encode(buf, pathSegment, StandardCharsets.US_ASCII); buf.append('/'); } return buf.toString(); } } catch (final URISyntaxException ex) { return path; } } } TunnelRefusedException.java000066400000000000000000000034061434266521000370460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.core5.http.HttpException; /** * Signals that the tunnel request was rejected by the proxy host. * * @since 4.0 */ public class TunnelRefusedException extends HttpException { private static final long serialVersionUID = -8646722842745617323L; private final String responseMessage; public TunnelRefusedException(final String message, final String responseMessage) { super(message); this.responseMessage = responseMessage; } public String getResponseMessage() { return this.responseMessage; } } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/Wire.java000066400000000000000000000127351434266521000333760ustar00rootroot00000000000000/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.nio.ByteBuffer; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; @Internal public class Wire { private static final int MAX_STRING_BUILDER_SIZE = 2048; private static final ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); /** * Returns a {@code StringBuilder} that this Layout implementation can use to write the formatted log event to. * * @return a {@code StringBuilder} */ private static StringBuilder getStringBuilder() { StringBuilder result = THREAD_LOCAL.get(); if (result == null) { result = new StringBuilder(MAX_STRING_BUILDER_SIZE); THREAD_LOCAL.set(result); } trimToMaxSize(result, MAX_STRING_BUILDER_SIZE); result.setLength(0); return result; } /** * Ensures that the char[] array of the specified StringBuilder does not exceed the specified number of characters. * This method is useful to ensure that excessively long char[] arrays are not kept in memory forever. * * @param stringBuilder the StringBuilder to check * @param maxSize the maximum number of characters the StringBuilder is allowed to have */ private static void trimToMaxSize(final StringBuilder stringBuilder, final int maxSize) { if (stringBuilder != null && stringBuilder.capacity() > maxSize) { stringBuilder.setLength(maxSize); stringBuilder.trimToSize(); } } private final Logger log; private final String id; public Wire(final Logger 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 = getStringBuilder(); 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); log.debug("{} {}", this.id, buffer); 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); log.debug("{} {}", this.id, buffer); } } public boolean isEnabled() { return log.isDebugEnabled(); } public void output(final byte[] b, final int pos, final int off) { Args.notNull(b, "Output"); wire(">> ", b, pos, off); } public void input(final byte[] b, final int pos, final int off) { Args.notNull(b, "Input"); wire("<< ", b, pos, off); } public void output(final byte[] b) { Args.notNull(b, "Output"); output(b, 0, b.length); } public void input(final byte[] b) { Args.notNull(b, "Input"); 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 String s) { Args.notNull(s, "Output"); output(s.getBytes()); } public void input(final String s) { Args.notNull(s, "Input"); input(s.getBytes()); } public void output(final ByteBuffer b) { Args.notNull(b, "Output"); 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) { Args.notNull(b, "Input"); 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); } } } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/000077500000000000000000000000001434266521000327325ustar00rootroot00000000000000AbstractHttpAsyncClientBase.java000066400000000000000000000101701434266521000410500ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOReactorStatus; import org.apache.hc.core5.util.TimeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; abstract class AbstractHttpAsyncClientBase extends CloseableHttpAsyncClient { enum Status { READY, RUNNING, TERMINATED } private static final Logger LOG = LoggerFactory.getLogger(AbstractHttpAsyncClientBase.class); private final AsyncPushConsumerRegistry pushConsumerRegistry; private final DefaultConnectingIOReactor ioReactor; private final ExecutorService executorService; private final AtomicReference status; AbstractHttpAsyncClientBase( final DefaultConnectingIOReactor ioReactor, final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory) { super(); this.ioReactor = ioReactor; this.pushConsumerRegistry = pushConsumerRegistry; this.executorService = Executors.newSingleThreadExecutor(threadFactory); this.status = new AtomicReference<>(Status.READY); } @Override public final void start() { if (status.compareAndSet(Status.READY, Status.RUNNING)) { executorService.execute(ioReactor::start); } } @Override public void register(final String hostname, final String uriPattern, final Supplier supplier) { pushConsumerRegistry.register(hostname, uriPattern, supplier); } boolean isRunning() { return status.get() == Status.RUNNING; } ConnectionInitiator getConnectionInitiator() { return ioReactor; } @Override public final IOReactorStatus getStatus() { return ioReactor.getStatus(); } @Override public final void awaitShutdown(final TimeValue waitTime) throws InterruptedException { ioReactor.awaitShutdown(waitTime); } @Override public final void initiateShutdown() { if (LOG.isDebugEnabled()) { LOG.debug("Initiating shutdown"); } ioReactor.initiateShutdown(); } void internalClose(final CloseMode closeMode) { } @Override public final void close(final CloseMode closeMode) { if (LOG.isDebugEnabled()) { LOG.debug("Shutdown {}", closeMode); } ioReactor.initiateShutdown(); ioReactor.close(closeMode); executorService.shutdownNow(); internalClose(closeMode); } @Override public void close() { close(CloseMode.GRACEFUL); } } AbstractMinimalHttpAsyncClientBase.java000066400000000000000000000075601434266521000423700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.support.BasicClientExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; abstract class AbstractMinimalHttpAsyncClientBase extends AbstractHttpAsyncClientBase { AbstractMinimalHttpAsyncClientBase( final DefaultConnectingIOReactor ioReactor, final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory) { super(ioReactor, pushConsumerRegistry, threadFactory); } @Override protected Future doExecute( final HttpHost httpHost, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback) { final ComplexFuture future = new ComplexFuture<>(callback); future.setDependency(execute(new BasicClientExchangeHandler<>( requestProducer, responseConsumer, new FutureCallback() { @Override public void completed(final T result) { future.completed(result); } @Override public void failed(final Exception ex) { future.failed(ex); } @Override public void cancelled() { future.cancel(); } }), pushHandlerFactory, context)); return future; } public final Cancellable execute(final AsyncClientExchangeHandler exchangeHandler) { return execute(exchangeHandler, null, HttpClientContext.create()); } public abstract Cancellable execute( AsyncClientExchangeHandler exchangeHandler, HandlerFactory pushHandlerFactory, HttpContext context); } AsyncConnectExec.java000066400000000000000000000475121434266521000367230ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.io.InterruptedIOException; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteTracker; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.TunnelRefusedException; import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper; import org.apache.hc.client5.http.impl.auth.HttpAuthenticator; import org.apache.hc.client5.http.impl.routing.BasicRouteDirector; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRouteDirector; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request execution handler in the asynchronous request execution chain * that is responsible for establishing connection to the target * origin server as specified by the current connection route. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class AsyncConnectExec implements AsyncExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(AsyncConnectExec.class); private final HttpProcessor proxyHttpProcessor; private final AuthenticationStrategy proxyAuthStrategy; private final HttpAuthenticator authenticator; private final AuthCacheKeeper authCacheKeeper; private final HttpRouteDirector routeDirector; public AsyncConnectExec( final HttpProcessor proxyHttpProcessor, final AuthenticationStrategy proxyAuthStrategy, final SchemePortResolver schemePortResolver, final boolean authCachingDisabled) { Args.notNull(proxyHttpProcessor, "Proxy HTTP processor"); Args.notNull(proxyAuthStrategy, "Proxy authentication strategy"); this.proxyHttpProcessor = proxyHttpProcessor; this.proxyAuthStrategy = proxyAuthStrategy; this.authenticator = new HttpAuthenticator(); this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(schemePortResolver); this.routeDirector = BasicRouteDirector.INSTANCE; } static class State { State(final HttpRoute route) { tracker = new RouteTracker(route); } final RouteTracker tracker; volatile boolean challenged; volatile boolean tunnelRefused; } @Override public void execute( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final CancellableDependency cancellableDependency = scope.cancellableDependency; final HttpClientContext clientContext = scope.clientContext; final AsyncExecRuntime execRuntime = scope.execRuntime; final State state = new State(route); if (!execRuntime.isEndpointAcquired()) { final Object userToken = clientContext.getUserToken(); if (LOG.isDebugEnabled()) { LOG.debug("{} acquiring connection with route {}", exchangeId, route); } cancellableDependency.setDependency(execRuntime.acquireEndpoint( exchangeId, route, userToken, clientContext, new FutureCallback() { @Override public void completed(final AsyncExecRuntime execRuntime) { if (execRuntime.isEndpointConnected()) { try { chain.proceed(request, entityProducer, scope, asyncExecCallback); } catch (final HttpException | IOException ex) { asyncExecCallback.failed(ex); } } else { proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback); } } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); } else { if (execRuntime.isEndpointConnected()) { try { chain.proceed(request, entityProducer, scope, asyncExecCallback); } catch (final HttpException | IOException ex) { asyncExecCallback.failed(ex); } } else { proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback); } } } private void proceedToNextHop( final State state, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) { final RouteTracker tracker = state.tracker; final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final AsyncExecRuntime execRuntime = scope.execRuntime; final CancellableDependency operation = scope.cancellableDependency; final HttpClientContext clientContext = scope.clientContext; final HttpRoute fact = tracker.toRoute(); final int step = routeDirector.nextStep(route, fact); switch (step) { case HttpRouteDirector.CONNECT_TARGET: operation.setDependency(execRuntime.connectEndpoint(clientContext, new FutureCallback() { @Override public void completed(final AsyncExecRuntime execRuntime) { tracker.connectTarget(route.isSecure()); if (LOG.isDebugEnabled()) { LOG.debug("{} connected to target", exchangeId); } proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback); } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); break; case HttpRouteDirector.CONNECT_PROXY: operation.setDependency(execRuntime.connectEndpoint(clientContext, new FutureCallback() { @Override public void completed(final AsyncExecRuntime execRuntime) { final HttpHost proxy = route.getProxyHost(); tracker.connectProxy(proxy, route.isSecure() && !route.isTunnelled()); if (LOG.isDebugEnabled()) { LOG.debug("{} connected to proxy", exchangeId); } proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback); } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } })); break; case HttpRouteDirector.TUNNEL_TARGET: try { final HttpHost proxy = route.getProxyHost(); final HttpHost target = route.getTargetHost(); if (LOG.isDebugEnabled()) { LOG.debug("{} create tunnel", exchangeId); } createTunnel(state, proxy, target, scope, chain, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { return asyncExecCallback.handleResponse(response, entityDetails); } @Override public void handleInformationResponse( final HttpResponse response) throws HttpException, IOException { asyncExecCallback.handleInformationResponse(response); } @Override public void completed() { if (!execRuntime.isEndpointConnected()) { // Remote endpoint disconnected. Need to start over if (LOG.isDebugEnabled()) { LOG.debug("{} proxy disconnected", exchangeId); } state.tracker.reset(); } if (state.challenged) { if (LOG.isDebugEnabled()) { LOG.debug("{} proxy authentication required", exchangeId); } proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback); } else { if (state.tunnelRefused) { if (LOG.isDebugEnabled()) { LOG.debug("{} tunnel refused", exchangeId); } asyncExecCallback.failed(new TunnelRefusedException("Tunnel refused", null)); } else { if (LOG.isDebugEnabled()) { LOG.debug("{} tunnel to target created", exchangeId); } tracker.tunnelTarget(false); proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback); } } } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } }); } catch (final HttpException | IOException ex) { asyncExecCallback.failed(ex); } break; case HttpRouteDirector.TUNNEL_PROXY: // The most simple example for this case is a proxy chain // of two proxies, where P1 must be tunnelled to P2. // route: Source -> P1 -> P2 -> Target (3 hops) // fact: Source -> P1 -> Target (2 hops) asyncExecCallback.failed(new HttpException("Proxy chains are not supported")); break; case HttpRouteDirector.LAYER_PROTOCOL: execRuntime.upgradeTls(clientContext, new FutureCallback() { @Override public void completed(final AsyncExecRuntime asyncExecRuntime) { if (LOG.isDebugEnabled()) { LOG.debug("{} upgraded to TLS", exchangeId); } tracker.layerProtocol(route.isSecure()); proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback); } @Override public void failed(final Exception ex) { asyncExecCallback.failed(ex); } @Override public void cancelled() { asyncExecCallback.failed(new InterruptedIOException()); } }); break; case HttpRouteDirector.UNREACHABLE: asyncExecCallback.failed(new HttpException("Unable to establish route: " + "planned = " + route + "; current = " + fact)); break; case HttpRouteDirector.COMPLETE: if (LOG.isDebugEnabled()) { LOG.debug("{} route fully established", exchangeId); } try { chain.proceed(request, entityProducer, scope, asyncExecCallback); } catch (final HttpException | IOException ex) { asyncExecCallback.failed(ex); } break; default: throw new IllegalStateException("Unknown step indicator " + step + " from RouteDirector."); } } private void createTunnel( final State state, final HttpHost proxy, final HttpHost nextHop, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final HttpClientContext clientContext = scope.clientContext; final AuthExchange proxyAuthExchange = proxy != null ? clientContext.getAuthExchange(proxy) : new AuthExchange(); if (authCacheKeeper != null) { authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, clientContext); } final HttpRequest connect = new BasicHttpRequest(Method.CONNECT, nextHop, nextHop.toHostString()); connect.setVersion(HttpVersion.HTTP_1_1); proxyHttpProcessor.process(connect, null, clientContext); authenticator.addAuthResponse(proxy, ChallengeType.PROXY, connect, proxyAuthExchange, clientContext); chain.proceed(connect, null, scope, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); proxyHttpProcessor.process(response, entityDetails, clientContext); final int status = response.getCode(); if (status < HttpStatus.SC_SUCCESS) { throw new HttpException("Unexpected response to CONNECT request: " + new StatusLine(response)); } if (needAuthentication(proxyAuthExchange, proxy, response, clientContext)) { state.challenged = true; return null; } state.challenged = false; if (status >= HttpStatus.SC_REDIRECTION) { state.tunnelRefused = true; return asyncExecCallback.handleResponse(response, entityDetails); } return null; } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { } @Override public void completed() { asyncExecCallback.completed(); } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } }); } private boolean needAuthentication( final AuthExchange proxyAuthExchange, final HttpHost proxy, final HttpResponse response, final HttpClientContext context) { final RequestConfig config = context.getRequestConfig(); if (config.isAuthenticationEnabled()) { final boolean proxyAuthRequested = authenticator.isChallenged(proxy, ChallengeType.PROXY, response, proxyAuthExchange, context); if (authCacheKeeper != null) { if (proxyAuthRequested) { authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context); } else { authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context); } } if (proxyAuthRequested) { final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response, proxyAuthStrategy, proxyAuthExchange, context); if (authCacheKeeper != null) { authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context); } return updated; } } return false; } } AsyncExecChainElement.java000066400000000000000000000046611434266521000376640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; class AsyncExecChainElement { private final AsyncExecChainHandler handler; private final AsyncExecChainElement next; AsyncExecChainElement(final AsyncExecChainHandler handler, final AsyncExecChainElement next) { this.handler = handler; this.next = next; } public void execute( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { handler.execute(request, entityProducer, scope, next != null ? next::execute : null, asyncExecCallback); } @Override public String toString() { return "{" + "handler=" + handler.getClass() + ", next=" + (next != null ? next.handler.getClass() : "null") + '}'; } }AsyncHttpRequestRetryExec.java000066400000000000000000000202161434266521000406400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request executor in the asynchronous request execution chain that is * responsible for making a decision whether a request that failed due to * an I/O exception or received a specific response from the target server should * be re-executed. Note that this exec chain handler will not respect * {@link HttpRequestRetryStrategy#getRetryInterval(HttpResponse, int, org.apache.hc.core5.http.protocol.HttpContext)}. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class AsyncHttpRequestRetryExec implements AsyncExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(AsyncHttpRequestRetryExec.class); private final HttpRequestRetryStrategy retryStrategy; public AsyncHttpRequestRetryExec(final HttpRequestRetryStrategy retryStrategy) { Args.notNull(retryStrategy, "retryStrategy"); this.retryStrategy = retryStrategy; } private static class State { volatile boolean retrying; volatile TimeValue delay; } private void internalExecute( final State state, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final String exchangeId = scope.exchangeId; chain.proceed(BasicRequestBuilder.copy(request).build(), entityProducer, scope, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { final HttpClientContext clientContext = scope.clientContext; if (entityProducer != null && !entityProducer.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} cannot retry non-repeatable request", exchangeId); } return asyncExecCallback.handleResponse(response, entityDetails); } state.retrying = retryStrategy.retryRequest(response, scope.execCount.get(), clientContext); if (state.retrying) { state.delay = retryStrategy.getRetryInterval(response, scope.execCount.get(), clientContext); if (LOG.isDebugEnabled()) { LOG.debug("{} retrying request in {}", exchangeId, state.delay); } return new DiscardingEntityConsumer<>(); } else { return asyncExecCallback.handleResponse(response, entityDetails); } } @Override public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException { asyncExecCallback.handleInformationResponse(response); } @Override public void completed() { if (state.retrying) { scope.execCount.incrementAndGet(); if (entityProducer != null) { entityProducer.releaseResources(); } scope.scheduler.scheduleExecution(request, entityProducer, scope, asyncExecCallback, state.delay); } else { asyncExecCallback.completed(); } } @Override public void failed(final Exception cause) { if (cause instanceof IOException) { final HttpRoute route = scope.route; final HttpClientContext clientContext = scope.clientContext; if (entityProducer != null && !entityProducer.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} cannot retry non-repeatable request", exchangeId); } } else if (retryStrategy.retryRequest(request, (IOException) cause, scope.execCount.get(), clientContext)) { if (LOG.isDebugEnabled()) { LOG.debug("{} {}", exchangeId, cause.getMessage(), cause); } if (LOG.isInfoEnabled()) { LOG.info("Recoverable I/O exception ({}) caught when processing request to {}", cause.getClass().getName(), route); } scope.execRuntime.discardEndpoint(); if (entityProducer != null) { entityProducer.releaseResources(); } state.retrying = true; final int execCount = scope.execCount.incrementAndGet(); state.delay = retryStrategy.getRetryInterval(request, (IOException) cause, execCount - 1, clientContext); scope.scheduler.scheduleExecution(request, entityProducer, scope, asyncExecCallback, state.delay); return; } } asyncExecCallback.failed(cause); } }); } @Override public void execute( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final State state = new State(); state.retrying = false; internalExecute(state, request, entityProducer, scope, chain, asyncExecCallback); } } AsyncProtocolExec.java000066400000000000000000000372221434266521000371300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.RequestSupport; import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper; import org.apache.hc.client5.http.impl.auth.HttpAuthenticator; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request execution handler in the asynchronous request execution chain * that is responsible for implementation of HTTP specification requirements. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class AsyncProtocolExec implements AsyncExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(AsyncProtocolExec.class); private final AuthenticationStrategy targetAuthStrategy; private final AuthenticationStrategy proxyAuthStrategy; private final HttpAuthenticator authenticator; private final SchemePortResolver schemePortResolver; private final AuthCacheKeeper authCacheKeeper; AsyncProtocolExec( final AuthenticationStrategy targetAuthStrategy, final AuthenticationStrategy proxyAuthStrategy, final SchemePortResolver schemePortResolver, final boolean authCachingDisabled) { this.targetAuthStrategy = Args.notNull(targetAuthStrategy, "Target authentication strategy"); this.proxyAuthStrategy = Args.notNull(proxyAuthStrategy, "Proxy authentication strategy"); this.authenticator = new HttpAuthenticator(); this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(this.schemePortResolver); } @Override public void execute( final HttpRequest userRequest, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { if (Method.CONNECT.isSame(userRequest.getMethod())) { throw new ProtocolException("Direct execution of CONNECT is not allowed"); } final HttpRoute route = scope.route; final HttpHost routeTarget = route.getTargetHost(); final HttpHost proxy = route.getProxyHost(); final HttpClientContext clientContext = scope.clientContext; final HttpRequest request; if (proxy != null && !route.isTunnelled()) { final BasicRequestBuilder requestBuilder = BasicRequestBuilder.copy(userRequest); if (requestBuilder.getAuthority() == null) { requestBuilder.setAuthority(new URIAuthority(routeTarget)); } requestBuilder.setAbsoluteRequestUri(true); request = requestBuilder.build(); } else { request = userRequest; } // Ensure the request has a scheme and an authority if (request.getScheme() == null) { request.setScheme(routeTarget.getSchemeName()); } if (request.getAuthority() == null) { request.setAuthority(new URIAuthority(routeTarget)); } final URIAuthority authority = request.getAuthority(); if (authority.getUserInfo() != null) { throw new ProtocolException("Request URI authority contains deprecated userinfo component"); } final HttpHost target = new HttpHost( request.getScheme(), authority.getHostName(), schemePortResolver.resolve(request.getScheme(), authority)); final String pathPrefix = RequestSupport.extractPathPrefix(request); final AuthExchange targetAuthExchange = clientContext.getAuthExchange(target); final AuthExchange proxyAuthExchange = proxy != null ? clientContext.getAuthExchange(proxy) : new AuthExchange(); if (!targetAuthExchange.isConnectionBased() && targetAuthExchange.getPathPrefix() != null && !pathPrefix.startsWith(targetAuthExchange.getPathPrefix())) { // force re-authentication if the current path prefix does not match // that of the previous authentication exchange. targetAuthExchange.reset(); } if (targetAuthExchange.getPathPrefix() == null) { targetAuthExchange.setPathPrefix(pathPrefix); } if (authCacheKeeper != null) { authCacheKeeper.loadPreemptively(target, pathPrefix, targetAuthExchange, clientContext); if (proxy != null) { authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, clientContext); } } final AtomicBoolean challenged = new AtomicBoolean(false); internalExecute(target, pathPrefix, targetAuthExchange, proxyAuthExchange, challenged, request, entityProducer, scope, chain, asyncExecCallback); } private void internalExecute( final HttpHost target, final String pathPrefix, final AuthExchange targetAuthExchange, final AuthExchange proxyAuthExchange, final AtomicBoolean challenged, final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final HttpClientContext clientContext = scope.clientContext; final AsyncExecRuntime execRuntime = scope.execRuntime; final HttpHost proxy = route.getProxyHost(); if (!request.containsHeader(HttpHeaders.AUTHORIZATION)) { if (LOG.isDebugEnabled()) { LOG.debug("{} target auth state: {}", exchangeId, targetAuthExchange.getState()); } authenticator.addAuthResponse(target, ChallengeType.TARGET, request, targetAuthExchange, clientContext); } if (!request.containsHeader(HttpHeaders.PROXY_AUTHORIZATION) && !route.isTunnelled()) { if (LOG.isDebugEnabled()) { LOG.debug("{} proxy auth state: {}", exchangeId, proxyAuthExchange.getState()); } authenticator.addAuthResponse(proxy, ChallengeType.PROXY, request, proxyAuthExchange, clientContext); } chain.proceed(request, entityProducer, scope, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { if (Method.TRACE.isSame(request.getMethod())) { // Do not perform authentication for TRACE request return asyncExecCallback.handleResponse(response, entityDetails); } if (needAuthentication( targetAuthExchange, proxyAuthExchange, proxy != null ? proxy : target, target, pathPrefix, response, clientContext)) { challenged.set(true); return null; } challenged.set(false); return asyncExecCallback.handleResponse(response, entityDetails); } @Override public void handleInformationResponse( final HttpResponse response) throws HttpException, IOException { asyncExecCallback.handleInformationResponse(response); } @Override public void completed() { if (!execRuntime.isEndpointConnected()) { if (proxyAuthExchange.getState() == AuthExchange.State.SUCCESS && proxyAuthExchange.isConnectionBased()) { if (LOG.isDebugEnabled()) { LOG.debug("{} resetting proxy auth state", exchangeId); } proxyAuthExchange.reset(); } if (targetAuthExchange.getState() == AuthExchange.State.SUCCESS && targetAuthExchange.isConnectionBased()) { if (LOG.isDebugEnabled()) { LOG.debug("{} resetting target auth state", exchangeId); } targetAuthExchange.reset(); } } if (challenged.get()) { if (entityProducer != null && !entityProducer.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} cannot retry non-repeatable request", exchangeId); } asyncExecCallback.completed(); } else { // Reset request headers final HttpRequest original = scope.originalRequest; request.setHeaders(); for (final Iterator
it = original.headerIterator(); it.hasNext(); ) { request.addHeader(it.next()); } try { if (entityProducer != null) { entityProducer.releaseResources(); } internalExecute(target, pathPrefix, targetAuthExchange, proxyAuthExchange, challenged, request, entityProducer, scope, chain, asyncExecCallback); } catch (final HttpException | IOException ex) { asyncExecCallback.failed(ex); } } } else { asyncExecCallback.completed(); } } @Override public void failed(final Exception cause) { if (cause instanceof IOException || cause instanceof RuntimeException) { for (final AuthExchange authExchange : clientContext.getAuthExchanges().values()) { if (authExchange.isConnectionBased()) { authExchange.reset(); } } } asyncExecCallback.failed(cause); } }); } private boolean needAuthentication( final AuthExchange targetAuthExchange, final AuthExchange proxyAuthExchange, final HttpHost proxy, final HttpHost target, final String pathPrefix, final HttpResponse response, final HttpClientContext context) { final RequestConfig config = context.getRequestConfig(); if (config.isAuthenticationEnabled()) { final boolean targetAuthRequested = authenticator.isChallenged( target, ChallengeType.TARGET, response, targetAuthExchange, context); if (authCacheKeeper != null) { if (targetAuthRequested) { authCacheKeeper.updateOnChallenge(target, pathPrefix, targetAuthExchange, context); } else { authCacheKeeper.updateOnNoChallenge(target, pathPrefix, targetAuthExchange, context); } } final boolean proxyAuthRequested = authenticator.isChallenged( proxy, ChallengeType.PROXY, response, proxyAuthExchange, context); if (authCacheKeeper != null) { if (proxyAuthRequested) { authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context); } else { authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context); } } if (targetAuthRequested) { final boolean updated = authenticator.updateAuthState(target, ChallengeType.TARGET, response, targetAuthStrategy, targetAuthExchange, context); if (authCacheKeeper != null) { authCacheKeeper.updateOnResponse(target, pathPrefix, targetAuthExchange, context); } return updated; } if (proxyAuthRequested) { final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response, proxyAuthStrategy, proxyAuthExchange, context); if (authCacheKeeper != null) { authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context); } return updated; } } return false; } } AsyncPushConsumerRegistry.java000066400000000000000000000076131434266521000407070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.protocol.UriPatternMatcher; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; class AsyncPushConsumerRegistry { private final UriPatternMatcher> primary; private final ConcurrentMap>> hostMap; public AsyncPushConsumerRegistry() { this.primary = new UriPatternMatcher<>(); this.hostMap = new ConcurrentHashMap<>(); } private UriPatternMatcher> getPatternMatcher(final String hostname) { if (hostname == null) { return primary; } final UriPatternMatcher> hostMatcher = hostMap.get(hostname); if (hostMatcher != null) { return hostMatcher; } return primary; } public AsyncPushConsumer get(final HttpRequest request) { Args.notNull(request, "Request"); final URIAuthority authority = request.getAuthority(); final String key = authority != null ? authority.getHostName().toLowerCase(Locale.ROOT) : null; final UriPatternMatcher> patternMatcher = getPatternMatcher(key); if (patternMatcher == null) { return null; } String path = request.getPath(); final int i = path.indexOf('?'); if (i != -1) { path = path.substring(0, i); } final Supplier supplier = patternMatcher.lookup(path); return supplier != null ? supplier.get() : null; } public void register(final String hostname, final String uriPattern, final Supplier supplier) { Args.notBlank(uriPattern, "URI pattern"); Args.notNull(supplier, "Supplier"); if (hostname == null) { primary.register(uriPattern, supplier); } else { final String key = hostname.toLowerCase(Locale.ROOT); UriPatternMatcher> matcher = hostMap.get(key); if (matcher == null) { final UriPatternMatcher> newMatcher = new UriPatternMatcher<>(); matcher = hostMap.putIfAbsent(key, newMatcher); if (matcher == null) { matcher = newMatcher; } } matcher.register(uriPattern, supplier); } } } AsyncRedirectExec.java000066400000000000000000000312331434266521000370640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.net.URI; import java.util.Objects; import org.apache.hc.client5.http.CircularRedirectException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RedirectException; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.RedirectLocations; import org.apache.hc.client5.http.protocol.RedirectStrategy; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request execution handler in the asynchronous request execution chain * responsible for handling of request redirects. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class AsyncRedirectExec implements AsyncExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(AsyncRedirectExec.class); private final HttpRoutePlanner routePlanner; private final RedirectStrategy redirectStrategy; AsyncRedirectExec(final HttpRoutePlanner routePlanner, final RedirectStrategy redirectStrategy) { this.routePlanner = routePlanner; this.redirectStrategy = redirectStrategy; } private static class State { volatile URI redirectURI; volatile int maxRedirects; volatile int redirectCount; volatile HttpRequest currentRequest; volatile AsyncEntityProducer currentEntityProducer; volatile RedirectLocations redirectLocations; volatile AsyncExecChain.Scope currentScope; volatile boolean reroute; } private void internalExecute( final State state, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final HttpRequest request = state.currentRequest; final AsyncEntityProducer entityProducer = state.currentEntityProducer; final AsyncExecChain.Scope scope = state.currentScope; final HttpClientContext clientContext = scope.clientContext; final String exchangeId = scope.exchangeId; final HttpRoute currentRoute = scope.route; chain.proceed(request, entityProducer, scope, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { state.redirectURI = null; final RequestConfig config = clientContext.getRequestConfig(); if (config.isRedirectsEnabled() && redirectStrategy.isRedirected(request, response, clientContext)) { if (state.redirectCount >= state.maxRedirects) { throw new RedirectException("Maximum redirects (" + state.maxRedirects + ") exceeded"); } state.redirectCount++; final URI redirectUri = redirectStrategy.getLocationURI(request, response, clientContext); if (LOG.isDebugEnabled()) { LOG.debug("{} redirect requested to location '{}'", exchangeId, redirectUri); } if (!config.isCircularRedirectsAllowed()) { if (state.redirectLocations.contains(redirectUri)) { throw new CircularRedirectException("Circular redirect to '" + redirectUri + "'"); } } state.redirectLocations.add(redirectUri); final HttpHost newTarget = URIUtils.extractHost(redirectUri); if (newTarget == null) { throw new ProtocolException("Redirect URI does not specify a valid host name: " + redirectUri); } final int statusCode = response.getCode(); final BasicRequestBuilder redirectBuilder; switch (statusCode) { case HttpStatus.SC_MOVED_PERMANENTLY: case HttpStatus.SC_MOVED_TEMPORARILY: if (Method.POST.isSame(request.getMethod())) { redirectBuilder = BasicRequestBuilder.get(); state.currentEntityProducer = null; } else { redirectBuilder = BasicRequestBuilder.copy(scope.originalRequest); } break; case HttpStatus.SC_SEE_OTHER: if (!Method.GET.isSame(request.getMethod()) && !Method.HEAD.isSame(request.getMethod())) { redirectBuilder = BasicRequestBuilder.get(); state.currentEntityProducer = null; } else { redirectBuilder = BasicRequestBuilder.copy(scope.originalRequest); } break; default: redirectBuilder = BasicRequestBuilder.copy(scope.originalRequest); } redirectBuilder.setUri(redirectUri); state.reroute = false; state.redirectURI = redirectUri; state.currentRequest = redirectBuilder.build(); if (!Objects.equals(currentRoute.getTargetHost(), newTarget)) { final HttpRoute newRoute = routePlanner.determineRoute(newTarget, clientContext); if (!Objects.equals(currentRoute, newRoute)) { state.reroute = true; final AuthExchange targetAuthExchange = clientContext.getAuthExchange(currentRoute.getTargetHost()); if (LOG.isDebugEnabled()) { LOG.debug("{} resetting target auth state", exchangeId); } targetAuthExchange.reset(); if (currentRoute.getProxyHost() != null) { final AuthExchange proxyAuthExchange = clientContext.getAuthExchange(currentRoute.getProxyHost()); if (proxyAuthExchange.isConnectionBased()) { if (LOG.isDebugEnabled()) { LOG.debug("{} resetting proxy auth state", exchangeId); } proxyAuthExchange.reset(); } } state.currentScope = new AsyncExecChain.Scope( scope.exchangeId, newRoute, scope.originalRequest, scope.cancellableDependency, scope.clientContext, scope.execRuntime, scope.scheduler, scope.execCount); } } } if (state.redirectURI != null) { if (LOG.isDebugEnabled()) { LOG.debug("{} redirecting to '{}' via {}", exchangeId, state.redirectURI, currentRoute); } return null; } return asyncExecCallback.handleResponse(response, entityDetails); } @Override public void handleInformationResponse( final HttpResponse response) throws HttpException, IOException { asyncExecCallback.handleInformationResponse(response); } @Override public void completed() { if (state.redirectURI == null) { asyncExecCallback.completed(); } else { final AsyncEntityProducer entityProducer = state.currentEntityProducer; if (entityProducer != null) { entityProducer.releaseResources(); } if (entityProducer != null && !entityProducer.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} cannot redirect non-repeatable request", exchangeId); } asyncExecCallback.completed(); } else { try { if (state.reroute) { scope.execRuntime.releaseEndpoint(); } internalExecute(state, chain, asyncExecCallback); } catch (final IOException | HttpException ex) { asyncExecCallback.failed(ex); } } } } @Override public void failed(final Exception cause) { asyncExecCallback.failed(cause); } }); } @Override public void execute( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final HttpClientContext clientContext = scope.clientContext; RedirectLocations redirectLocations = clientContext.getRedirectLocations(); if (redirectLocations == null) { redirectLocations = new RedirectLocations(); clientContext.setAttribute(HttpClientContext.REDIRECT_LOCATIONS, redirectLocations); } redirectLocations.clear(); final RequestConfig config = clientContext.getRequestConfig(); final State state = new State(); state.maxRedirects = config.getMaxRedirects() > 0 ? config.getMaxRedirects() : 50; state.redirectCount = 0; state.currentRequest = request; state.currentEntityProducer = entityProducer; state.redirectLocations = redirectLocations; state.currentScope = scope; internalExecute(state, chain, asyncExecCallback); } } CloseableHttpAsyncClient.java000066400000000000000000000137511434266521000404130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.HttpAsyncClient; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.reactor.IOReactorStatus; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** * Base implementation of {@link HttpAsyncClient} that also implements {@link ModalCloseable}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public abstract class CloseableHttpAsyncClient implements HttpAsyncClient, ModalCloseable { public abstract void start(); public abstract IOReactorStatus getStatus(); public abstract void awaitShutdown(TimeValue waitTime) throws InterruptedException; public abstract void initiateShutdown(); protected abstract Future doExecute( final HttpHost target, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback); public final Future execute( final HttpHost target, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback) { Args.notNull(requestProducer, "Request producer"); Args.notNull(responseConsumer, "Response consumer"); return doExecute(target, requestProducer, responseConsumer, pushHandlerFactory, context, callback); } @Override public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback) { Args.notNull(requestProducer, "Request producer"); Args.notNull(responseConsumer, "Response consumer"); return doExecute(null, requestProducer, responseConsumer, pushHandlerFactory, context, callback); } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HttpContext context, final FutureCallback callback) { Args.notNull(requestProducer, "Request producer"); Args.notNull(responseConsumer, "Response consumer"); return execute(requestProducer, responseConsumer, null, context, callback); } public final Future execute( final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final FutureCallback callback) { Args.notNull(requestProducer, "Request producer"); Args.notNull(responseConsumer, "Response consumer"); return execute(requestProducer, responseConsumer, HttpClientContext.create(), callback); } public final Future execute( final SimpleHttpRequest request, final HttpContext context, final FutureCallback callback) { Args.notNull(request, "Request"); return execute(SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), context, callback); } public final Future execute( final SimpleHttpRequest request, final FutureCallback callback) { return execute(request, HttpClientContext.create(), callback); } public abstract void register(String hostname, String uriPattern, Supplier supplier); public final void register(final String uriPattern, final Supplier supplier) { register(null, uriPattern, supplier); } } H2AsyncClientBuilder.java000066400000000000000000001066711434266521000374460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.Closeable; import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ThreadFactory; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.CookieSpecSupport; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory; import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory; import org.apache.hc.client5.http.impl.auth.KerberosSchemeFactory; import org.apache.hc.client5.http.impl.auth.NTLMSchemeFactory; import org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory; import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider; import org.apache.hc.client5.http.impl.nio.MultihomeConnectionInitiator; import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner; import org.apache.hc.client5.http.protocol.RedirectStrategy; import org.apache.hc.client5.http.protocol.RequestAddCookies; import org.apache.hc.client5.http.protocol.RequestDefaultHeaders; import org.apache.hc.client5.http.protocol.RequestExpectContinue; import org.apache.hc.client5.http.protocol.ResponseProcessCookies; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.protocol.H2RequestConnControl; import org.apache.hc.core5.http2.protocol.H2RequestContent; import org.apache.hc.core5.http2.protocol.H2RequestTargetHost; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.VersionInfo; /** * Builder for HTTP/2 only {@link CloseableHttpAsyncClient} instances. *

* Concurrent message exchanges with the same connection route executed * with these {@link CloseableHttpAsyncClient} instances will get * automatically multiplexed over a single physical HTTP/2 connection. *

*

* When a particular component is not explicitly set this class will * use its default implementation. *

* * @since 5.0 */ public class H2AsyncClientBuilder { private static class RequestInterceptorEntry { enum Position { FIRST, LAST } final RequestInterceptorEntry.Position position; final HttpRequestInterceptor interceptor; private RequestInterceptorEntry(final RequestInterceptorEntry.Position position, final HttpRequestInterceptor interceptor) { this.position = position; this.interceptor = interceptor; } } private static class ResponseInterceptorEntry { enum Position { FIRST, LAST } final ResponseInterceptorEntry.Position position; final HttpResponseInterceptor interceptor; private ResponseInterceptorEntry(final ResponseInterceptorEntry.Position position, final HttpResponseInterceptor interceptor) { this.position = position; this.interceptor = interceptor; } } private static class ExecInterceptorEntry { enum Position { BEFORE, AFTER, REPLACE, FIRST, LAST } final ExecInterceptorEntry.Position position; final String name; final AsyncExecChainHandler interceptor; final String existing; private ExecInterceptorEntry( final ExecInterceptorEntry.Position position, final String name, final AsyncExecChainHandler interceptor, final String existing) { this.position = position; this.name = name; this.interceptor = interceptor; this.existing = existing; } } private IOReactorConfig ioReactorConfig; private IOSessionListener ioSessionListener; private H2Config h2Config; private CharCodingConfig charCodingConfig; private SchemePortResolver schemePortResolver; private AuthenticationStrategy targetAuthStrategy; private AuthenticationStrategy proxyAuthStrategy; private LinkedList requestInterceptors; private LinkedList responseInterceptors; private LinkedList execInterceptors; private HttpRoutePlanner routePlanner; private RedirectStrategy redirectStrategy; private HttpRequestRetryStrategy retryStrategy; private Lookup authSchemeRegistry; private Lookup cookieSpecRegistry; private CookieStore cookieStore; private CredentialsProvider credentialsProvider; private String userAgent; private Collection defaultHeaders; private RequestConfig defaultRequestConfig; private Resolver connectionConfigResolver; private boolean evictIdleConnections; private TimeValue maxIdleTime; private boolean systemProperties; private boolean automaticRetriesDisabled; private boolean redirectHandlingDisabled; private boolean cookieManagementDisabled; private boolean authCachingDisabled; private DnsResolver dnsResolver; private TlsStrategy tlsStrategy; private ThreadFactory threadFactory; private List closeables; private Callback ioReactorExceptionCallback; private Decorator ioSessionDecorator; public static H2AsyncClientBuilder create() { return new H2AsyncClientBuilder(); } protected H2AsyncClientBuilder() { super(); } /** * Sets {@link H2Config} configuration. */ public final H2AsyncClientBuilder setH2Config(final H2Config h2Config) { this.h2Config = h2Config; return this; } /** * Sets {@link IOReactorConfig} configuration. */ public final H2AsyncClientBuilder setIOReactorConfig(final IOReactorConfig ioReactorConfig) { this.ioReactorConfig = ioReactorConfig; return this; } /** * Sets {@link IOSessionListener} listener. * * @since 5.2 */ public final H2AsyncClientBuilder setIOSessionListener(final IOSessionListener ioSessionListener) { this.ioSessionListener = ioSessionListener; return this; } /** * Sets {@link CharCodingConfig} configuration. */ public final H2AsyncClientBuilder setCharCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } /** * Assigns {@link AuthenticationStrategy} instance for target * host authentication. */ public final H2AsyncClientBuilder setTargetAuthenticationStrategy( final AuthenticationStrategy targetAuthStrategy) { this.targetAuthStrategy = targetAuthStrategy; return this; } /** * Assigns {@link AuthenticationStrategy} instance for proxy * authentication. */ public final H2AsyncClientBuilder setProxyAuthenticationStrategy( final AuthenticationStrategy proxyAuthStrategy) { this.proxyAuthStrategy = proxyAuthStrategy; return this; } /** * Sets the callback that will be invoked when the client's IOReactor encounters an uncaught exception. * * @since 5.2 */ public final H2AsyncClientBuilder setIoReactorExceptionCallback(final Callback ioReactorExceptionCallback) { this.ioReactorExceptionCallback = ioReactorExceptionCallback; return this; } /** * Sets the {@link IOSession} {@link Decorator} that will be use with the client's IOReactor. * * @since 5.2 */ public final H2AsyncClientBuilder setIoSessionDecorator(final Decorator ioSessionDecorator) { this.ioSessionDecorator = ioSessionDecorator; return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. */ public final H2AsyncClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (responseInterceptors == null) { responseInterceptors = new LinkedList<>(); } responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.FIRST, interceptor)); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. */ public final H2AsyncClientBuilder addResponseInterceptorLast(final HttpResponseInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (responseInterceptors == null) { responseInterceptors = new LinkedList<>(); } responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.LAST, interceptor)); return this; } /** * Adds this execution interceptor before an existing interceptor. */ public final H2AsyncClientBuilder addExecInterceptorBefore(final String existing, final String name, final AsyncExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.BEFORE, name, interceptor, existing)); return this; } /** * Adds this execution interceptor after interceptor with the given name. */ public final H2AsyncClientBuilder addExecInterceptorAfter(final String existing, final String name, final AsyncExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.AFTER, name, interceptor, existing)); return this; } /** * Replace an existing interceptor with the given name with new interceptor. */ public final H2AsyncClientBuilder replaceExecInterceptor(final String existing, final AsyncExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.REPLACE, existing, interceptor, existing)); return this; } /** * Add an interceptor to the head of the processing list. */ public final H2AsyncClientBuilder addExecInterceptorFirst(final String name, final AsyncExecChainHandler interceptor) { Args.notNull(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.FIRST, name, interceptor, null)); return this; } /** * Add an interceptor to the tail of the processing list. */ public final H2AsyncClientBuilder addExecInterceptorLast(final String name, final AsyncExecChainHandler interceptor) { Args.notNull(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.LAST, name, interceptor, null)); return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. */ public final H2AsyncClientBuilder addRequestInterceptorFirst(final HttpRequestInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (requestInterceptors == null) { requestInterceptors = new LinkedList<>(); } requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.FIRST, interceptor)); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. */ public final H2AsyncClientBuilder addRequestInterceptorLast(final HttpRequestInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (requestInterceptors == null) { requestInterceptors = new LinkedList<>(); } requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.LAST, interceptor)); return this; } /** * Assigns {@link HttpRequestRetryStrategy} instance. *

* Please note this value can be overridden by the {@link #disableAutomaticRetries()} * method. */ public final H2AsyncClientBuilder setRetryStrategy(final HttpRequestRetryStrategy retryStrategy) { this.retryStrategy = retryStrategy; return this; } /** * Assigns {@link RedirectStrategy} instance. *

* Please note this value can be overridden by the {@link #disableRedirectHandling()} * method. *

*/ public H2AsyncClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) { this.redirectStrategy = redirectStrategy; return this; } /** * Assigns {@link SchemePortResolver} instance. */ public final H2AsyncClientBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) { this.schemePortResolver = schemePortResolver; return this; } /** * Assigns {@link DnsResolver} instance. */ public final H2AsyncClientBuilder setDnsResolver(final DnsResolver dnsResolver) { this.dnsResolver = dnsResolver; return this; } /** * Assigns {@link TlsStrategy} instance. */ public final H2AsyncClientBuilder setTlsStrategy(final TlsStrategy tlsStrategy) { this.tlsStrategy = tlsStrategy; return this; } /** * Assigns {@link ThreadFactory} instance. */ public final H2AsyncClientBuilder setThreadFactory(final ThreadFactory threadFactory) { this.threadFactory = threadFactory; return this; } /** * Assigns {@code User-Agent} value. */ public final H2AsyncClientBuilder setUserAgent(final String userAgent) { this.userAgent = userAgent; return this; } /** * Assigns default request header values. */ public final H2AsyncClientBuilder setDefaultHeaders(final Collection defaultHeaders) { this.defaultHeaders = defaultHeaders; return this; } /** * Assigns {@link HttpRoutePlanner} instance. */ public final H2AsyncClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) { this.routePlanner = routePlanner; 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 H2AsyncClientBuilder setDefaultCredentialsProvider(final CredentialsProvider credentialsProvider) { this.credentialsProvider = credentialsProvider; return this; } /** * Assigns default {@link org.apache.hc.client5.http.auth.AuthScheme} registry which will * be used for request execution if not explicitly set in the client execution * context. */ public final H2AsyncClientBuilder setDefaultAuthSchemeRegistry(final Lookup authSchemeRegistry) { this.authSchemeRegistry = authSchemeRegistry; return this; } /** * Assigns default {@link org.apache.hc.client5.http.cookie.CookieSpec} registry * which will be used for request execution if not explicitly set in the client * execution context. */ public final H2AsyncClientBuilder setDefaultCookieSpecRegistry(final Lookup cookieSpecRegistry) { this.cookieSpecRegistry = cookieSpecRegistry; 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 H2AsyncClientBuilder setDefaultCookieStore(final CookieStore cookieStore) { this.cookieStore = cookieStore; 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 H2AsyncClientBuilder setDefaultRequestConfig(final RequestConfig config) { this.defaultRequestConfig = config; return this; } /** * Assigns {@link Resolver} for {@link ConnectionConfig} on a per host basis. * * @since 5.2 */ public final H2AsyncClientBuilder setConnectionConfigResolver(final Resolver connectionConfigResolver) { this.connectionConfigResolver = connectionConfigResolver; return this; } /** * Assigns the same {@link ConnectionConfig} for all hosts. * * @since 5.2 */ public final H2AsyncClientBuilder setDefaultConnectionConfig(final ConnectionConfig connectionConfig) { this.connectionConfigResolver = (host) -> connectionConfig; return this; } /** * Use system properties when creating and configuring default * implementations. */ public final H2AsyncClientBuilder useSystemProperties() { this.systemProperties = true; return this; } /** * Disables automatic redirect handling. */ public final H2AsyncClientBuilder disableRedirectHandling() { redirectHandlingDisabled = true; return this; } /** * Disables automatic request recovery and re-execution. */ public final H2AsyncClientBuilder disableAutomaticRetries() { automaticRetriesDisabled = true; return this; } /** * Disables state (cookie) management. */ public final H2AsyncClientBuilder disableCookieManagement() { this.cookieManagementDisabled = true; return this; } /** * Disables authentication scheme caching. */ public final H2AsyncClientBuilder disableAuthCaching() { this.authCachingDisabled = true; return this; } /** * Makes this instance of HttpClient proactively evict idle connections from the * connection pool using a background thread. *

* One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()} * in order to stop and release the background thread. *

* Please note this method has no effect if the instance of HttpClient is configured to * use a shared connection manager. * * @param maxIdleTime maximum time persistent connections can stay idle while kept alive * in the connection pool. Connections whose inactivity period exceeds this value will * get closed and evicted from the pool. */ public final H2AsyncClientBuilder evictIdleConnections(final TimeValue maxIdleTime) { this.evictIdleConnections = true; this.maxIdleTime = maxIdleTime; return this; } /** * Request exec chain customization and extension. *

* For internal use. */ @Internal protected void customizeExecChain(final NamedElementChain execChainDefinition) { } /** * Adds to the list of {@link Closeable} resources to be managed by the client. *

* For internal use. */ @Internal protected void addCloseable(final Closeable closeable) { if (closeable == null) { return; } if (closeables == null) { closeables = new ArrayList<>(); } closeables.add(closeable); } public CloseableHttpAsyncClient build() { AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy; if (targetAuthStrategyCopy == null) { targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy; if (proxyAuthStrategyCopy == null) { proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } String userAgentCopy = this.userAgent; if (userAgentCopy == null) { if (systemProperties) { userAgentCopy = getProperty("http.agent", null); } if (userAgentCopy == null) { userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpAsyncClient", "org.apache.hc.client5", getClass()); } } final HttpProcessorBuilder b = HttpProcessorBuilder.create(); if (requestInterceptors != null) { for (final RequestInterceptorEntry entry: requestInterceptors) { if (entry.position == RequestInterceptorEntry.Position.FIRST) { b.addFirst(entry.interceptor); } } } if (responseInterceptors != null) { for (final ResponseInterceptorEntry entry: responseInterceptors) { if (entry.position == ResponseInterceptorEntry.Position.FIRST) { b.addFirst(entry.interceptor); } } } b.addAll( new RequestDefaultHeaders(defaultHeaders), new RequestUserAgent(userAgentCopy), new RequestExpectContinue(), new H2RequestContent(), new H2RequestTargetHost(), new H2RequestConnControl()); if (!cookieManagementDisabled) { b.add(RequestAddCookies.INSTANCE); } if (!cookieManagementDisabled) { b.add(ResponseProcessCookies.INSTANCE); } if (requestInterceptors != null) { for (final RequestInterceptorEntry entry: requestInterceptors) { if (entry.position == RequestInterceptorEntry.Position.LAST) { b.addLast(entry.interceptor); } } } if (responseInterceptors != null) { for (final ResponseInterceptorEntry entry: responseInterceptors) { if (entry.position == ResponseInterceptorEntry.Position.LAST) { b.addLast(entry.interceptor); } } } final HttpProcessor httpProcessor = b.build(); final NamedElementChain execChainDefinition = new NamedElementChain<>(); execChainDefinition.addLast( new H2AsyncMainClientExec(httpProcessor), ChainElement.MAIN_TRANSPORT.name()); execChainDefinition.addFirst( new AsyncConnectExec( new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)), proxyAuthStrategyCopy, schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE, authCachingDisabled), ChainElement.CONNECT.name()); execChainDefinition.addFirst( new AsyncProtocolExec( targetAuthStrategyCopy, proxyAuthStrategyCopy, schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE, authCachingDisabled), ChainElement.PROTOCOL.name()); // Add request retry executor, if not disabled if (!automaticRetriesDisabled) { HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy; if (retryStrategyCopy == null) { retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE; } execChainDefinition.addFirst( new AsyncHttpRequestRetryExec(retryStrategyCopy), ChainElement.RETRY.name()); } HttpRoutePlanner routePlannerCopy = this.routePlanner; if (routePlannerCopy == null) { SchemePortResolver schemePortResolverCopy = this.schemePortResolver; if (schemePortResolverCopy == null) { schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE; } routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy); } // Add redirect executor, if not disabled if (!redirectHandlingDisabled) { RedirectStrategy redirectStrategyCopy = this.redirectStrategy; if (redirectStrategyCopy == null) { redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE; } execChainDefinition.addFirst( new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy), ChainElement.REDIRECT.name()); } final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry(); final IOEventHandlerFactory ioEventHandlerFactory = new H2AsyncClientProtocolStarter( HttpProcessorBuilder.create().build(), (request, context) -> pushConsumerRegistry.get(request), h2Config != null ? h2Config : H2Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT); final DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor( ioEventHandlerFactory, ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT, threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-dispatch", true), ioSessionDecorator != null ? ioSessionDecorator : LoggingIOSessionDecorator.INSTANCE, ioReactorExceptionCallback != null ? ioReactorExceptionCallback : LoggingExceptionCallback.INSTANCE, ioSessionListener, ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.IMMEDIATE)); if (execInterceptors != null) { for (final ExecInterceptorEntry entry: execInterceptors) { switch (entry.position) { case AFTER: execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name); break; case BEFORE: execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name); break; case REPLACE: execChainDefinition.replace(entry.existing, entry.interceptor); break; case FIRST: execChainDefinition.addFirst(entry.interceptor, entry.name); break; case LAST: // Don't add last, after H2AsyncMainClientExec, as that does not delegate to the chain // Instead, add the interceptor just before it, making it effectively the last interceptor execChainDefinition.addBefore(ChainElement.MAIN_TRANSPORT.name(), entry.interceptor, entry.name); break; } } } customizeExecChain(execChainDefinition); NamedElementChain.Node current = execChainDefinition.getLast(); AsyncExecChainElement execChain = null; while (current != null) { execChain = new AsyncExecChainElement(current.getValue(), execChain); current = current.getPrevious(); } Lookup authSchemeRegistryCopy = this.authSchemeRegistry; if (authSchemeRegistryCopy == null) { authSchemeRegistryCopy = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE) .register(StandardAuthScheme.NTLM, NTLMSchemeFactory.INSTANCE) .register(StandardAuthScheme.SPNEGO, SPNegoSchemeFactory.DEFAULT) .register(StandardAuthScheme.KERBEROS, KerberosSchemeFactory.DEFAULT) .build(); } Lookup cookieSpecRegistryCopy = this.cookieSpecRegistry; if (cookieSpecRegistryCopy == null) { cookieSpecRegistryCopy = CookieSpecSupport.createDefault(); } CookieStore cookieStoreCopy = this.cookieStore; if (cookieStoreCopy == null) { cookieStoreCopy = new BasicCookieStore(); } CredentialsProvider credentialsProviderCopy = this.credentialsProvider; if (credentialsProviderCopy == null) { if (systemProperties) { credentialsProviderCopy = new SystemDefaultCredentialsProvider(); } else { credentialsProviderCopy = new BasicCredentialsProvider(); } } TlsStrategy tlsStrategyCopy = this.tlsStrategy; if (tlsStrategyCopy == null) { if (systemProperties) { tlsStrategyCopy = DefaultClientTlsStrategy.getSystemDefault(); } else { tlsStrategyCopy = DefaultClientTlsStrategy.getDefault(); } } final MultihomeConnectionInitiator connectionInitiator = new MultihomeConnectionInitiator(ioReactor, dnsResolver); final InternalH2ConnPool connPool = new InternalH2ConnPool(connectionInitiator, host -> null, tlsStrategyCopy); connPool.setConnectionConfigResolver(connectionConfigResolver); List closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null; if (closeablesCopy == null) { closeablesCopy = new ArrayList<>(1); } if (evictIdleConnections) { final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(connPool, maxIdleTime != null ? maxIdleTime : TimeValue.ofSeconds(30L)); closeablesCopy.add(connectionEvictor::shutdown); connectionEvictor.start(); } closeablesCopy.add(connPool); return new InternalH2AsyncClient( ioReactor, execChain, pushConsumerRegistry, threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true), connPool, routePlannerCopy, cookieSpecRegistryCopy, authSchemeRegistryCopy, cookieStoreCopy, credentialsProviderCopy, defaultRequestConfig, closeablesCopy); } private static String getProperty(final String key, final String defaultValue) { return AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(key, defaultValue)); } static class IdleConnectionEvictor implements Closeable { private final Thread thread; public IdleConnectionEvictor(final InternalH2ConnPool connPool, final TimeValue maxIdleTime) { this.thread = new DefaultThreadFactory("idle-connection-evictor", true).newThread(() -> { try { while (!Thread.currentThread().isInterrupted()) { maxIdleTime.sleep(); connPool.closeIdle(maxIdleTime); } } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } catch (final Exception ex) { } }); } public void start() { thread.start(); } public void shutdown() { thread.interrupt(); } @Override public void close() throws IOException { shutdown(); } } } H2AsyncClientProtocolStarter.java000066400000000000000000000211741434266521000412200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.FramePrinter; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.ClientH2PrefaceHandler; import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class H2AsyncClientProtocolStarter implements IOEventHandlerFactory { private static final Logger HEADER_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.headers"); private static final Logger FRAME_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.frame"); private static final Logger FRAME_PAYLOAD_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.frame.payload"); private static final Logger FLOW_CTRL_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.flow"); private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final H2Config h2Config; private final CharCodingConfig charCodingConfig; H2AsyncClientProtocolStarter( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final H2Config h2Config, final CharCodingConfig charCodingConfig) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = exchangeHandlerFactory; this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { if (HEADER_LOG.isDebugEnabled() || FRAME_LOG.isDebugEnabled() || FRAME_PAYLOAD_LOG.isDebugEnabled() || FLOW_CTRL_LOG.isDebugEnabled()) { final String id = ioSession.getId(); final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory( httpProcessor, exchangeHandlerFactory, h2Config, charCodingConfig, new H2StreamListener() { final FramePrinter framePrinter = new FramePrinter(); private void logFrameInfo(final String prefix, final RawFrame frame) { try { final LogAppendable logAppendable = new LogAppendable(FRAME_LOG, prefix); framePrinter.printFrameInfo(frame, logAppendable); logAppendable.flush(); } catch (final IOException ignore) { } } private void logFramePayload(final String prefix, final RawFrame frame) { try { final LogAppendable logAppendable = new LogAppendable(FRAME_PAYLOAD_LOG, prefix); framePrinter.printPayload(frame, logAppendable); logAppendable.flush(); } catch (final IOException ignore) { } } private void logFlowControl(final String prefix, final int streamId, final int delta, final int actualSize) { FLOW_CTRL_LOG.debug("{} stream {} flow control {} -> {}", prefix, streamId, delta, actualSize); } @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { if (HEADER_LOG.isDebugEnabled()) { for (int i = 0; i < headers.size(); i++) { HEADER_LOG.debug("{} << {}", id, headers.get(i)); } } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { if (HEADER_LOG.isDebugEnabled()) { for (int i = 0; i < headers.size(); i++) { HEADER_LOG.debug("{} >> {}", id, headers.get(i)); } } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { if (FRAME_LOG.isDebugEnabled()) { logFrameInfo(id + " <<", frame); } if (FRAME_PAYLOAD_LOG.isDebugEnabled()) { logFramePayload(id + " <<", frame); } } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { if (FRAME_LOG.isDebugEnabled()) { logFrameInfo(id + " >>", frame); } if (FRAME_PAYLOAD_LOG.isDebugEnabled()) { logFramePayload(id + " >>", frame); } } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (FLOW_CTRL_LOG.isDebugEnabled()) { logFlowControl(id + " <<", streamId, delta, actualSize); } } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (FLOW_CTRL_LOG.isDebugEnabled()) { logFlowControl(id + " >>", streamId, delta, actualSize); } } }); return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false); } final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory( httpProcessor, exchangeHandlerFactory, h2Config, charCodingConfig, null); return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false); } } H2AsyncMainClientExec.java000066400000000000000000000205741434266521000375460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Usually the last HTTP/2 request execution handler in the asynchronous * request execution chain that is responsible for execution of * request / response exchanges with the opposite endpoint. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public class H2AsyncMainClientExec implements AsyncExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(H2AsyncMainClientExec.class); private final HttpProcessor httpProcessor; H2AsyncMainClientExec(final HttpProcessor httpProcessor) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP protocol processor"); } @Override public void execute( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final CancellableDependency operation = scope.cancellableDependency; final HttpClientContext clientContext = scope.clientContext; final AsyncExecRuntime execRuntime = scope.execRuntime; if (LOG.isDebugEnabled()) { LOG.debug("{} executing {}", exchangeId, new RequestLine(request)); } final AsyncClientExchangeHandler internalExchangeHandler = new AsyncClientExchangeHandler() { private final AtomicReference entityConsumerRef = new AtomicReference<>(); @Override public void releaseResources() { final AsyncDataConsumer entityConsumer = entityConsumerRef.getAndSet(null); if (entityConsumer != null) { entityConsumer.releaseResources(); } } @Override public void failed(final Exception cause) { final AsyncDataConsumer entityConsumer = entityConsumerRef.getAndSet(null); if (entityConsumer != null) { entityConsumer.releaseResources(); } execRuntime.markConnectionNonReusable(); asyncExecCallback.failed(cause); } @Override public void cancel() { failed(new InterruptedIOException()); } @Override public void produceRequest(final RequestChannel channel, final HttpContext context) throws HttpException, IOException { clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, route); clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request); httpProcessor.process(request, entityProducer, clientContext); channel.sendRequest(request, entityProducer, context); } @Override public int available() { return entityProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { entityProducer.produce(channel); } @Override public void consumeInformation(final HttpResponse response, final HttpContext context) throws HttpException, IOException { } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, entityDetails, clientContext); entityConsumerRef.set(asyncExecCallback.handleResponse(response, entityDetails)); if (entityDetails == null) { execRuntime.validateConnection(); asyncExecCallback.completed(); } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncDataConsumer entityConsumer = entityConsumerRef.get(); if (entityConsumer != null) { entityConsumer.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public void consume(final ByteBuffer src) throws IOException { final AsyncDataConsumer entityConsumer = entityConsumerRef.get(); if (entityConsumer != null) { entityConsumer.consume(src); } } @Override public void streamEnd(final List trailers) throws HttpException, IOException { final AsyncDataConsumer entityConsumer = entityConsumerRef.getAndSet(null); if (entityConsumer != null) { entityConsumer.streamEnd(trailers); } else { execRuntime.validateConnection(); } asyncExecCallback.completed(); } }; if (LOG.isDebugEnabled()) { operation.setDependency(execRuntime.execute( exchangeId, new LoggingAsyncClientExchangeHandler(LOG, exchangeId, internalExchangeHandler), clientContext)); } else { operation.setDependency(execRuntime.execute(exchangeId, internalExchangeHandler, clientContext)); } } } HttpAsyncClientBuilder.java000066400000000000000000001210101434266521000400740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.Closeable; import java.net.ProxySelector; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ThreadFactory; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.CookieSpecSupport; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy; import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.DefaultUserTokenHandler; import org.apache.hc.client5.http.impl.IdleConnectionEvictor; import org.apache.hc.client5.http.impl.NoopUserTokenHandler; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory; import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory; import org.apache.hc.client5.http.impl.auth.KerberosSchemeFactory; import org.apache.hc.client5.http.impl.auth.NTLMSchemeFactory; import org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory; import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner; import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner; import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner; import org.apache.hc.client5.http.nio.AsyncClientConnectionManager; import org.apache.hc.client5.http.protocol.RedirectStrategy; import org.apache.hc.client5.http.protocol.RequestAddCookies; import org.apache.hc.client5.http.protocol.RequestDefaultHeaders; import org.apache.hc.client5.http.protocol.RequestExpectContinue; import org.apache.hc.client5.http.protocol.ResponseProcessCookies; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.protocol.H2RequestConnControl; import org.apache.hc.core5.http2.protocol.H2RequestContent; import org.apache.hc.core5.http2.protocol.H2RequestTargetHost; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.IOSessionListener; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.VersionInfo; /** * Builder for {@link CloseableHttpAsyncClient} instances that can negotiate * the most optimal HTTP protocol version during the {@code TLS} handshake * with {@code ALPN} extension if supported by the Java runtime. *

* Concurrent message exchanges executed by {@link CloseableHttpAsyncClient} * instances created with this builder will get automatically assigned to * separate connections leased from the connection pool. *

*

* When a particular component is not explicitly set 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()}. *

*
    *
  • http.proxyHost
  • *
  • http.proxyPort
  • *
  • https.proxyHost
  • *
  • https.proxyPort
  • *
  • http.nonProxyHosts
  • *
  • http.keepAlive
  • *
  • http.agent
  • *
*

* Please note that some settings used by this class can be mutually * exclusive and may not apply when building {@link CloseableHttpAsyncClient} * instances. *

* * @since 5.0 */ public class HttpAsyncClientBuilder { private static class RequestInterceptorEntry { enum Position { FIRST, LAST } final RequestInterceptorEntry.Position position; final HttpRequestInterceptor interceptor; private RequestInterceptorEntry(final RequestInterceptorEntry.Position position, final HttpRequestInterceptor interceptor) { this.position = position; this.interceptor = interceptor; } } private static class ResponseInterceptorEntry { enum Position { FIRST, LAST } final ResponseInterceptorEntry.Position position; final HttpResponseInterceptor interceptor; private ResponseInterceptorEntry(final ResponseInterceptorEntry.Position position, final HttpResponseInterceptor interceptor) { this.position = position; this.interceptor = interceptor; } } private static class ExecInterceptorEntry { enum Position { BEFORE, AFTER, REPLACE, FIRST, LAST } final ExecInterceptorEntry.Position position; final String name; final AsyncExecChainHandler interceptor; final String existing; private ExecInterceptorEntry( final ExecInterceptorEntry.Position position, final String name, final AsyncExecChainHandler interceptor, final String existing) { this.position = position; this.name = name; this.interceptor = interceptor; this.existing = existing; } } /** * @deprecated TLS should be configured by the connection manager */ @Deprecated private TlsConfig tlsConfig; private AsyncClientConnectionManager connManager; private boolean connManagerShared; private IOReactorConfig ioReactorConfig; private IOSessionListener ioSessionListener; private Callback ioReactorExceptionCallback; private Http1Config h1Config; private H2Config h2Config; private CharCodingConfig charCodingConfig; private SchemePortResolver schemePortResolver; private ConnectionKeepAliveStrategy keepAliveStrategy; private UserTokenHandler userTokenHandler; private AuthenticationStrategy targetAuthStrategy; private AuthenticationStrategy proxyAuthStrategy; private Decorator ioSessionDecorator; private LinkedList requestInterceptors; private LinkedList responseInterceptors; private LinkedList execInterceptors; private HttpRoutePlanner routePlanner; private RedirectStrategy redirectStrategy; private HttpRequestRetryStrategy retryStrategy; private ConnectionReuseStrategy reuseStrategy; private Lookup authSchemeRegistry; private Lookup cookieSpecRegistry; private CookieStore cookieStore; private CredentialsProvider credentialsProvider; private String userAgent; private HttpHost proxy; private Collection defaultHeaders; private RequestConfig defaultRequestConfig; private boolean evictExpiredConnections; private boolean evictIdleConnections; private TimeValue maxIdleTime; private boolean systemProperties; private boolean automaticRetriesDisabled; private boolean redirectHandlingDisabled; private boolean cookieManagementDisabled; private boolean authCachingDisabled; private boolean connectionStateDisabled; private ThreadFactory threadFactory; private List closeables; public static HttpAsyncClientBuilder create() { return new HttpAsyncClientBuilder(); } protected HttpAsyncClientBuilder() { super(); } /** * Sets HTTP protocol version policy. * * @deprecated Use {@link TlsConfig} and connection manager methods */ @Deprecated public final HttpAsyncClientBuilder setVersionPolicy(final HttpVersionPolicy versionPolicy) { this.tlsConfig = versionPolicy != null ? TlsConfig.custom().setVersionPolicy(versionPolicy).build() : null; return this; } /** * Sets {@link Http1Config} configuration. */ public final HttpAsyncClientBuilder setHttp1Config(final Http1Config h1Config) { this.h1Config = h1Config; return this; } /** * Sets {@link H2Config} configuration. */ public final HttpAsyncClientBuilder setH2Config(final H2Config h2Config) { this.h2Config = h2Config; return this; } /** * Assigns {@link AsyncClientConnectionManager} instance. */ public final HttpAsyncClientBuilder setConnectionManager(final AsyncClientConnectionManager connManager) { this.connManager = connManager; return this; } /** * Defines the connection manager is to be shared by multiple * client instances. *

* 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. */ public final HttpAsyncClientBuilder setConnectionManagerShared(final boolean shared) { this.connManagerShared = shared; return this; } /** * Sets {@link IOReactorConfig} configuration. */ public final HttpAsyncClientBuilder setIOReactorConfig(final IOReactorConfig ioReactorConfig) { this.ioReactorConfig = ioReactorConfig; return this; } /** * Sets {@link IOSessionListener} listener. * * @since 5.2 */ public final HttpAsyncClientBuilder setIOSessionListener(final IOSessionListener ioSessionListener) { this.ioSessionListener = ioSessionListener; return this; } /** * Sets the callback that will be invoked when the client's IOReactor encounters an uncaught exception. * * @since 5.1 */ public final HttpAsyncClientBuilder setIoReactorExceptionCallback(final Callback ioReactorExceptionCallback) { this.ioReactorExceptionCallback = ioReactorExceptionCallback; return this; } /** * Sets {@link CharCodingConfig} configuration. */ public final HttpAsyncClientBuilder setCharCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } /** * Assigns {@link ConnectionReuseStrategy} instance. *

* Please note this strategy applies to HTTP/1.0 and HTTP/1.1 connections only */ 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. *

* 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 target * host authentication. */ public final HttpAsyncClientBuilder setTargetAuthenticationStrategy( final AuthenticationStrategy targetAuthStrategy) { this.targetAuthStrategy = targetAuthStrategy; return this; } /** * Assigns {@link AuthenticationStrategy} instance for proxy * authentication. */ public final HttpAsyncClientBuilder setProxyAuthenticationStrategy( final AuthenticationStrategy proxyAuthStrategy) { this.proxyAuthStrategy = proxyAuthStrategy; return this; } /** * Sets the {@link IOSession} {@link Decorator} that will be use with the client's IOReactor. * * @since 5.2 */ public final HttpAsyncClientBuilder setIoSessionDecorator(final Decorator ioSessionDecorator) { this.ioSessionDecorator = ioSessionDecorator; return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. */ public final HttpAsyncClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (responseInterceptors == null) { responseInterceptors = new LinkedList<>(); } responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.FIRST, interceptor)); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. */ public final HttpAsyncClientBuilder addResponseInterceptorLast(final HttpResponseInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (responseInterceptors == null) { responseInterceptors = new LinkedList<>(); } responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.LAST, interceptor)); return this; } /** * Adds this execution interceptor before an existing interceptor. */ public final HttpAsyncClientBuilder addExecInterceptorBefore(final String existing, final String name, final AsyncExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.BEFORE, name, interceptor, existing)); return this; } /** * Adds this execution interceptor after interceptor with the given name. */ public final HttpAsyncClientBuilder addExecInterceptorAfter(final String existing, final String name, final AsyncExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.AFTER, name, interceptor, existing)); return this; } /** * Replace an existing interceptor with the given name with new interceptor. */ public final HttpAsyncClientBuilder replaceExecInterceptor(final String existing, final AsyncExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.REPLACE, existing, interceptor, existing)); return this; } /** * Add an interceptor to the head of the processing list. */ public final HttpAsyncClientBuilder addExecInterceptorFirst(final String name, final AsyncExecChainHandler interceptor) { Args.notNull(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.FIRST, name, interceptor, null)); return this; } /** * Add an interceptor to the tail of the processing list. */ public final HttpAsyncClientBuilder addExecInterceptorLast(final String name, final AsyncExecChainHandler interceptor) { Args.notNull(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.LAST, name, interceptor, null)); return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. */ public final HttpAsyncClientBuilder addRequestInterceptorFirst(final HttpRequestInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (requestInterceptors == null) { requestInterceptors = new LinkedList<>(); } requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.FIRST, interceptor)); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. */ public final HttpAsyncClientBuilder addRequestInterceptorLast(final HttpRequestInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (requestInterceptors == null) { requestInterceptors = new LinkedList<>(); } requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.LAST, interceptor)); return this; } /** * Assigns {@link HttpRequestRetryStrategy} instance. *

* Please note this value can be overridden by the {@link #disableAutomaticRetries()} * method. */ public final HttpAsyncClientBuilder setRetryStrategy(final HttpRequestRetryStrategy retryStrategy) { this.retryStrategy = retryStrategy; return this; } /** * Assigns {@link RedirectStrategy} instance. *

* Please note this value can be overridden by the {@link #disableRedirectHandling()} * method. *

*/ public HttpAsyncClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) { this.redirectStrategy = redirectStrategy; return this; } /** * Assigns {@link SchemePortResolver} instance. */ public final HttpAsyncClientBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) { this.schemePortResolver = schemePortResolver; return this; } /** * Assigns {@link ThreadFactory} instance. */ public final HttpAsyncClientBuilder setThreadFactory(final ThreadFactory threadFactory) { this.threadFactory = threadFactory; return this; } /** * Assigns {@code User-Agent} value. */ public final HttpAsyncClientBuilder setUserAgent(final String userAgent) { this.userAgent = userAgent; return this; } /** * Assigns default request header values. */ public final HttpAsyncClientBuilder setDefaultHeaders(final Collection defaultHeaders) { this.defaultHeaders = defaultHeaders; return this; } /** * Assigns default proxy value. *

* Please note this value can be overridden by the {@link #setRoutePlanner( * HttpRoutePlanner)} method. */ public final HttpAsyncClientBuilder setProxy(final HttpHost proxy) { this.proxy = proxy; return this; } /** * Assigns {@link HttpRoutePlanner} instance. */ public final HttpAsyncClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) { this.routePlanner = routePlanner; 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.hc.client5.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 authSchemeRegistry) { this.authSchemeRegistry = authSchemeRegistry; return this; } /** * Assigns default {@link org.apache.hc.client5.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 cookieSpecRegistry) { this.cookieSpecRegistry = cookieSpecRegistry; 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 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; } /** * Use system properties when creating and configuring default * implementations. */ public final HttpAsyncClientBuilder useSystemProperties() { this.systemProperties = true; return this; } /** * Disables connection state tracking. */ public final HttpAsyncClientBuilder disableConnectionState() { connectionStateDisabled = true; return this; } /** * Disables automatic redirect handling. */ public final HttpAsyncClientBuilder disableRedirectHandling() { redirectHandlingDisabled = true; return this; } /** * Disables automatic request recovery and re-execution. */ public final HttpAsyncClientBuilder disableAutomaticRetries() { automaticRetriesDisabled = true; return this; } /** * Disables state (cookie) management. */ public final HttpAsyncClientBuilder disableCookieManagement() { this.cookieManagementDisabled = true; return this; } /** * Disables authentication scheme caching. */ public final HttpAsyncClientBuilder disableAuthCaching() { this.authCachingDisabled = true; return this; } /** * Makes this instance of HttpClient proactively evict expired connections from the * connection pool using a background thread. *

* One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()} in order * to stop and release the background thread. *

* Please note this method has no effect if the instance of HttpClient is configured to * use a shared connection manager. * * @see #setConnectionManagerShared(boolean) * @see ConnPoolControl#closeExpired() */ public final HttpAsyncClientBuilder evictExpiredConnections() { evictExpiredConnections = true; return this; } /** * Makes this instance of HttpClient proactively evict idle connections from the * connection pool using a background thread. *

* One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()} * in order to stop and release the background thread. *

* Please note this method has no effect if the instance of HttpClient is configured to * use a shared connection manager. * * @see #setConnectionManagerShared(boolean) * @see ConnPoolControl#closeIdle(TimeValue) * * @param maxIdleTime maximum time persistent connections can stay idle while kept alive * in the connection pool. Connections whose inactivity period exceeds this value will * get closed and evicted from the pool. */ public final HttpAsyncClientBuilder evictIdleConnections(final TimeValue maxIdleTime) { this.evictIdleConnections = true; this.maxIdleTime = maxIdleTime; return this; } /** * Request exec chain customization and extension. *

* For internal use. */ @Internal protected void customizeExecChain(final NamedElementChain execChainDefinition) { } /** * Adds to the list of {@link Closeable} resources to be managed by the client. *

* For internal use. */ @Internal protected void addCloseable(final Closeable closeable) { if (closeable == null) { return; } if (closeables == null) { closeables = new ArrayList<>(); } closeables.add(closeable); } @SuppressWarnings("deprecated") public CloseableHttpAsyncClient build() { AsyncClientConnectionManager connManagerCopy = this.connManager; if (connManagerCopy == null) { connManagerCopy = PoolingAsyncClientConnectionManagerBuilder.create().build(); } ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy; if (keepAliveStrategyCopy == null) { keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE; } UserTokenHandler userTokenHandlerCopy = this.userTokenHandler; if (userTokenHandlerCopy == null) { if (!connectionStateDisabled) { userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE; } else { userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE; } } AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy; if (targetAuthStrategyCopy == null) { targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy; if (proxyAuthStrategyCopy == null) { proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } String userAgentCopy = this.userAgent; if (userAgentCopy == null) { if (systemProperties) { userAgentCopy = getProperty("http.agent", null); } if (userAgentCopy == null) { userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpAsyncClient", "org.apache.hc.client5", getClass()); } } final HttpProcessorBuilder b = HttpProcessorBuilder.create(); if (requestInterceptors != null) { for (final RequestInterceptorEntry entry: requestInterceptors) { if (entry.position == RequestInterceptorEntry.Position.FIRST) { b.addFirst(entry.interceptor); } } } if (responseInterceptors != null) { for (final ResponseInterceptorEntry entry: responseInterceptors) { if (entry.position == ResponseInterceptorEntry.Position.FIRST) { b.addFirst(entry.interceptor); } } } b.addAll( new RequestDefaultHeaders(defaultHeaders), new RequestUserAgent(userAgentCopy), new RequestExpectContinue(), new H2RequestContent(), new H2RequestTargetHost(), new H2RequestConnControl()); if (!cookieManagementDisabled) { b.add(RequestAddCookies.INSTANCE); } if (!cookieManagementDisabled) { b.add(ResponseProcessCookies.INSTANCE); } if (requestInterceptors != null) { for (final RequestInterceptorEntry entry: requestInterceptors) { if (entry.position == RequestInterceptorEntry.Position.LAST) { b.addLast(entry.interceptor); } } } if (responseInterceptors != null) { for (final ResponseInterceptorEntry entry: responseInterceptors) { if (entry.position == ResponseInterceptorEntry.Position.LAST) { b.addLast(entry.interceptor); } } } final HttpProcessor httpProcessor = b.build(); final NamedElementChain execChainDefinition = new NamedElementChain<>(); execChainDefinition.addLast( new HttpAsyncMainClientExec(httpProcessor, keepAliveStrategyCopy, userTokenHandlerCopy), ChainElement.MAIN_TRANSPORT.name()); execChainDefinition.addFirst( new AsyncConnectExec( new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)), proxyAuthStrategyCopy, schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE, authCachingDisabled), ChainElement.CONNECT.name()); execChainDefinition.addFirst( new AsyncProtocolExec( targetAuthStrategyCopy, proxyAuthStrategyCopy, schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE, authCachingDisabled), ChainElement.PROTOCOL.name()); // Add request retry executor, if not disabled if (!automaticRetriesDisabled) { HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy; if (retryStrategyCopy == null) { retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE; } execChainDefinition.addFirst( new AsyncHttpRequestRetryExec(retryStrategyCopy), ChainElement.RETRY.name()); } HttpRoutePlanner routePlannerCopy = this.routePlanner; if (routePlannerCopy == null) { SchemePortResolver schemePortResolverCopy = this.schemePortResolver; if (schemePortResolverCopy == null) { schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE; } if (proxy != null) { routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy); } else if (systemProperties) { final ProxySelector defaultProxySelector = AccessController.doPrivileged((PrivilegedAction) ProxySelector::getDefault); routePlannerCopy = new SystemDefaultRoutePlanner( schemePortResolverCopy, defaultProxySelector); } else { routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy); } } // Add redirect executor, if not disabled if (!redirectHandlingDisabled) { RedirectStrategy redirectStrategyCopy = this.redirectStrategy; if (redirectStrategyCopy == null) { redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE; } execChainDefinition.addFirst( new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy), ChainElement.REDIRECT.name()); } List closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null; if (!this.connManagerShared) { if (closeablesCopy == null) { closeablesCopy = new ArrayList<>(1); } if (evictExpiredConnections || evictIdleConnections) { if (connManagerCopy instanceof ConnPoolControl) { final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl) connManagerCopy, maxIdleTime, maxIdleTime); closeablesCopy.add(connectionEvictor::shutdown); connectionEvictor.start(); } } closeablesCopy.add(connManagerCopy); } ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy; if (reuseStrategyCopy == null) { if (systemProperties) { final String s = getProperty("http.keepAlive", "true"); if ("true".equalsIgnoreCase(s)) { reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE; } else { reuseStrategyCopy = (request, response, context) -> false; } } else { reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE; } } final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry(); final IOEventHandlerFactory ioEventHandlerFactory = new HttpAsyncClientProtocolNegotiationStarter( HttpProcessorBuilder.create().build(), (request, context) -> pushConsumerRegistry.get(request), h2Config != null ? h2Config : H2Config.DEFAULT, h1Config != null ? h1Config : Http1Config.DEFAULT, charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT, reuseStrategyCopy); final DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor( ioEventHandlerFactory, ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT, threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-dispatch", true), ioSessionDecorator != null ? ioSessionDecorator : LoggingIOSessionDecorator.INSTANCE, ioReactorExceptionCallback != null ? ioReactorExceptionCallback : LoggingExceptionCallback.INSTANCE, ioSessionListener, ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.IMMEDIATE)); if (execInterceptors != null) { for (final ExecInterceptorEntry entry: execInterceptors) { switch (entry.position) { case AFTER: execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name); break; case BEFORE: execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name); break; case REPLACE: execChainDefinition.replace(entry.existing, entry.interceptor); break; case FIRST: execChainDefinition.addFirst(entry.interceptor, entry.name); break; case LAST: // Don't add last, after HttpAsyncMainClientExec, as that does not delegate to the chain // Instead, add the interceptor just before it, making it effectively the last interceptor execChainDefinition.addBefore(ChainElement.MAIN_TRANSPORT.name(), entry.interceptor, entry.name); break; } } } customizeExecChain(execChainDefinition); NamedElementChain.Node current = execChainDefinition.getLast(); AsyncExecChainElement execChain = null; while (current != null) { execChain = new AsyncExecChainElement(current.getValue(), execChain); current = current.getPrevious(); } Lookup authSchemeRegistryCopy = this.authSchemeRegistry; if (authSchemeRegistryCopy == null) { authSchemeRegistryCopy = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE) .register(StandardAuthScheme.NTLM, NTLMSchemeFactory.INSTANCE) .register(StandardAuthScheme.SPNEGO, SPNegoSchemeFactory.DEFAULT) .register(StandardAuthScheme.KERBEROS, KerberosSchemeFactory.DEFAULT) .build(); } Lookup cookieSpecRegistryCopy = this.cookieSpecRegistry; if (cookieSpecRegistryCopy == null) { cookieSpecRegistryCopy = CookieSpecSupport.createDefault(); } CookieStore cookieStoreCopy = this.cookieStore; if (cookieStoreCopy == null) { cookieStoreCopy = new BasicCookieStore(); } CredentialsProvider credentialsProviderCopy = this.credentialsProvider; if (credentialsProviderCopy == null) { if (systemProperties) { credentialsProviderCopy = new SystemDefaultCredentialsProvider(); } else { credentialsProviderCopy = new BasicCredentialsProvider(); } } return new InternalHttpAsyncClient( ioReactor, execChain, pushConsumerRegistry, threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true), connManagerCopy, routePlannerCopy, tlsConfig, cookieSpecRegistryCopy, authSchemeRegistryCopy, cookieStoreCopy, credentialsProviderCopy, defaultRequestConfig, closeablesCopy); } private String getProperty(final String key, final String defaultValue) { return AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(key, defaultValue)); } } HttpAsyncClientProtocolNegotiationStarter.java000066400000000000000000000335751434266521000440770ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.util.Iterator; import java.util.List; import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.Http1StreamListener; import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler; import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestWriterFactory; import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseParserFactory; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.NHttpMessageParserFactory; import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.FramePrinter; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.nio.ClientH2PrefaceHandler; import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory; import org.apache.hc.core5.http2.impl.nio.ClientH2UpgradeHandler; import org.apache.hc.core5.http2.impl.nio.ClientHttp1UpgradeHandler; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; import org.apache.hc.core5.http2.impl.nio.HttpProtocolNegotiator; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class HttpAsyncClientProtocolNegotiationStarter implements IOEventHandlerFactory { private static final Logger STREAM_LOG = LoggerFactory.getLogger(InternalHttpAsyncClient.class); private static final Logger HEADER_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.headers"); private static final Logger FRAME_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.frame"); private static final Logger FRAME_PAYLOAD_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.frame.payload"); private static final Logger FLOW_CTRL_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.flow"); private final HttpProcessor httpProcessor; private final HandlerFactory exchangeHandlerFactory; private final H2Config h2Config; private final Http1Config h1Config; private final CharCodingConfig charCodingConfig; private final ConnectionReuseStrategy http1ConnectionReuseStrategy; private final NHttpMessageParserFactory http1ResponseParserFactory; private final NHttpMessageWriterFactory http1RequestWriterFactory; HttpAsyncClientProtocolNegotiationStarter( final HttpProcessor httpProcessor, final HandlerFactory exchangeHandlerFactory, final H2Config h2Config, final Http1Config h1Config, final CharCodingConfig charCodingConfig, final ConnectionReuseStrategy connectionReuseStrategy) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor"); this.exchangeHandlerFactory = exchangeHandlerFactory; this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT; this.h1Config = h1Config != null ? h1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.http1ConnectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultClientConnectionReuseStrategy.INSTANCE; this.http1ResponseParserFactory = new DefaultHttpResponseParserFactory(h1Config); this.http1RequestWriterFactory = DefaultHttpRequestWriterFactory.INSTANCE; } @Override public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) { final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory; final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory; if (STREAM_LOG.isDebugEnabled() || HEADER_LOG.isDebugEnabled() || FRAME_LOG.isDebugEnabled() || FRAME_PAYLOAD_LOG.isDebugEnabled() || FLOW_CTRL_LOG.isDebugEnabled()) { final String id = ioSession.getId(); http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory( httpProcessor, h1Config, charCodingConfig, http1ConnectionReuseStrategy, http1ResponseParserFactory, http1RequestWriterFactory, new Http1StreamListener() { @Override public void onRequestHead(final HttpConnection connection, final HttpRequest request) { if (HEADER_LOG.isDebugEnabled()) { HEADER_LOG.debug("{} >> {}", id, new RequestLine(request)); for (final Iterator

it = request.headerIterator(); it.hasNext(); ) { HEADER_LOG.debug("{} >> {}", id, it.next()); } } } @Override public void onResponseHead(final HttpConnection connection, final HttpResponse response) { if (HEADER_LOG.isDebugEnabled()) { HEADER_LOG.debug("{} << {}", id, new StatusLine(response)); for (final Iterator
it = response.headerIterator(); it.hasNext(); ) { HEADER_LOG.debug("{} << {}", id, it.next()); } } } @Override public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) { if (STREAM_LOG.isDebugEnabled()) { if (keepAlive) { STREAM_LOG.debug("{} Connection is kept alive", id); } else { STREAM_LOG.debug("{} Connection is not kept alive", id); } } } }); http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory( httpProcessor, exchangeHandlerFactory, h2Config, charCodingConfig, new H2StreamListener() { final FramePrinter framePrinter = new FramePrinter(); private void logFrameInfo(final String prefix, final RawFrame frame) { try { final LogAppendable logAppendable = new LogAppendable(FRAME_LOG, prefix); framePrinter.printFrameInfo(frame, logAppendable); logAppendable.flush(); } catch (final IOException ignore) { } } private void logFramePayload(final String prefix, final RawFrame frame) { try { final LogAppendable logAppendable = new LogAppendable(FRAME_PAYLOAD_LOG, prefix); framePrinter.printPayload(frame, logAppendable); logAppendable.flush(); } catch (final IOException ignore) { } } private void logFlowControl(final String prefix, final int streamId, final int delta, final int actualSize) { FLOW_CTRL_LOG.debug("{} stream {} flow control {} -> {}", prefix, streamId, delta, actualSize); } @Override public void onHeaderInput(final HttpConnection connection, final int streamId, final List headers) { if (HEADER_LOG.isDebugEnabled()) { for (int i = 0; i < headers.size(); i++) { HEADER_LOG.debug("{} << {}", id, headers.get(i)); } } } @Override public void onHeaderOutput(final HttpConnection connection, final int streamId, final List headers) { if (HEADER_LOG.isDebugEnabled()) { for (int i = 0; i < headers.size(); i++) { HEADER_LOG.debug("{} >> {}", id, headers.get(i)); } } } @Override public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) { if (FRAME_LOG.isDebugEnabled()) { logFrameInfo(id + " <<", frame); } if (FRAME_PAYLOAD_LOG.isDebugEnabled()) { logFramePayload(id + " <<", frame); } } @Override public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) { if (FRAME_LOG.isDebugEnabled()) { logFrameInfo(id + " >>", frame); } if (FRAME_PAYLOAD_LOG.isDebugEnabled()) { logFramePayload(id + " >>", frame); } } @Override public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (FLOW_CTRL_LOG.isDebugEnabled()) { logFlowControl(id + " <<", streamId, delta, actualSize); } } @Override public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) { if (FLOW_CTRL_LOG.isDebugEnabled()) { logFlowControl(id + " >>", streamId, delta, actualSize); } } }); } else { http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory( httpProcessor, h1Config, charCodingConfig, http1ConnectionReuseStrategy, http1ResponseParserFactory, http1RequestWriterFactory, null); http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory( httpProcessor, exchangeHandlerFactory, h2Config, charCodingConfig, null); } ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ClientHttp1UpgradeHandler(http1StreamHandlerFactory)); ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ClientH2UpgradeHandler(http2StreamHandlerFactory)); final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE; switch (versionPolicy) { case FORCE_HTTP_2: return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false); case FORCE_HTTP_1: return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession)); default: return new HttpProtocolNegotiator(ioSession, null); } } } HttpAsyncClients.java000066400000000000000000000331761434266521000367670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.nio.AsyncClientConnectionManager; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.protocol.H2RequestConnControl; import org.apache.hc.core5.http2.protocol.H2RequestContent; import org.apache.hc.core5.http2.protocol.H2RequestTargetHost; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.VersionInfo; /** * Factory methods for {@link CloseableHttpAsyncClient} instances. * * @since 5.0 */ public final 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 and system properties. */ public static CloseableHttpAsyncClient createSystem() { return HttpAsyncClientBuilder.create().useSystemProperties().build(); } /** * Creates builder object for construction of custom HTTP/2 * {@link CloseableHttpAsyncClient} instances optimized for HTTP/2 protocol * and message multiplexing */ public static H2AsyncClientBuilder customHttp2() { return H2AsyncClientBuilder.create(); } /** * Creates HTTP/2 {@link CloseableHttpAsyncClient} instance with default configuration * optimized for HTTP/2 protocol and message multiplexing. */ public static CloseableHttpAsyncClient createHttp2Default() { return H2AsyncClientBuilder.create().build(); } /** * Creates HTTP/2 {@link CloseableHttpAsyncClient} instance with default configuration and * system properties optimized for HTTP/2 protocol and message multiplexing. */ public static CloseableHttpAsyncClient createHttp2System() { return H2AsyncClientBuilder.create().useSystemProperties().build(); } private static HttpProcessor createMinimalProtocolProcessor() { return new DefaultHttpProcessor( new H2RequestContent(), new H2RequestTargetHost(), new H2RequestConnControl(), new RequestUserAgent(VersionInfo.getSoftwareInfo( "Apache-HttpAsyncClient", "org.apache.hc.client5", HttpAsyncClients.class))); } private static MinimalHttpAsyncClient createMinimalHttpAsyncClientImpl( final IOEventHandlerFactory eventHandlerFactory, final AsyncPushConsumerRegistry pushConsumerRegistry, final IOReactorConfig ioReactorConfig, final AsyncClientConnectionManager connmgr, final SchemePortResolver schemePortResolver, final TlsConfig tlsConfig) { return new MinimalHttpAsyncClient( eventHandlerFactory, pushConsumerRegistry, ioReactorConfig, new DefaultThreadFactory("httpclient-main", true), new DefaultThreadFactory("httpclient-dispatch", true), connmgr, schemePortResolver, tlsConfig); } /** * Creates {@link MinimalHttpAsyncClient} instance optimized for * HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol * functionality. * * @deprecated Use {@link #createMinimal(H2Config, Http1Config, IOReactorConfig, AsyncClientConnectionManager)} */ @Deprecated public static MinimalHttpAsyncClient createMinimal( final HttpVersionPolicy versionPolicy, final H2Config h2Config, final Http1Config h1Config, final IOReactorConfig ioReactorConfig, final AsyncClientConnectionManager connmgr) { final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry(); return createMinimalHttpAsyncClientImpl( new HttpAsyncClientProtocolNegotiationStarter( createMinimalProtocolProcessor(), (request, context) -> pushConsumerRegistry.get(request), h2Config, h1Config, CharCodingConfig.DEFAULT, DefaultClientConnectionReuseStrategy.INSTANCE), pushConsumerRegistry, ioReactorConfig, connmgr, DefaultSchemePortResolver.INSTANCE, versionPolicy != null ? TlsConfig.custom().setVersionPolicy(versionPolicy).build() : null); } /** * Creates {@link MinimalHttpAsyncClient} instance optimized for * HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol * functionality. * * @since 5.2 */ public static MinimalHttpAsyncClient createMinimal( final H2Config h2Config, final Http1Config h1Config, final IOReactorConfig ioReactorConfig, final AsyncClientConnectionManager connmgr) { final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry(); return createMinimalHttpAsyncClientImpl( new HttpAsyncClientProtocolNegotiationStarter( createMinimalProtocolProcessor(), (request, context) -> pushConsumerRegistry.get(request), h2Config, h1Config, CharCodingConfig.DEFAULT, DefaultClientConnectionReuseStrategy.INSTANCE), pushConsumerRegistry, ioReactorConfig, connmgr, DefaultSchemePortResolver.INSTANCE, null); } /** * Creates {@link MinimalHttpAsyncClient} instance optimized for * HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol * functionality. * * @deprecated Use {@link #createMinimal(H2Config, Http1Config, IOReactorConfig)} */ @Deprecated public static MinimalHttpAsyncClient createMinimal( final HttpVersionPolicy versionPolicy, final H2Config h2Config, final Http1Config h1Config, final IOReactorConfig ioReactorConfig) { return createMinimal(versionPolicy, h2Config, h1Config, ioReactorConfig, PoolingAsyncClientConnectionManagerBuilder.create().build()); } /** * Creates {@link MinimalHttpAsyncClient} instance optimized for * HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol * functionality. */ public static MinimalHttpAsyncClient createMinimal( final H2Config h2Config, final Http1Config h1Config, final IOReactorConfig ioReactorConfig) { return createMinimal(h2Config, h1Config, ioReactorConfig, PoolingAsyncClientConnectionManagerBuilder.create().build()); } /** * Creates {@link MinimalHttpAsyncClient} instance optimized for * HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol * functionality. */ public static MinimalHttpAsyncClient createMinimal(final H2Config h2Config, final Http1Config h1Config) { return createMinimal(HttpVersionPolicy.NEGOTIATE, h2Config, h1Config, IOReactorConfig.DEFAULT); } /** * Creates {@link MinimalHttpAsyncClient} instance optimized for * HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol * functionality. */ public static MinimalHttpAsyncClient createMinimal() { return createMinimal(H2Config.DEFAULT, Http1Config.DEFAULT); } /** * Creates {@link MinimalHttpAsyncClient} instance optimized for * HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol * functionality. */ public static MinimalHttpAsyncClient createMinimal(final AsyncClientConnectionManager connManager) { return createMinimal( HttpVersionPolicy.NEGOTIATE, H2Config.DEFAULT, Http1Config.DEFAULT, IOReactorConfig.DEFAULT, connManager); } private static MinimalH2AsyncClient createMinimalHttp2AsyncClientImpl( final IOEventHandlerFactory eventHandlerFactory, final AsyncPushConsumerRegistry pushConsumerRegistry, final IOReactorConfig ioReactorConfig, final DnsResolver dnsResolver, final TlsStrategy tlsStrategy) { return new MinimalH2AsyncClient( eventHandlerFactory, pushConsumerRegistry, ioReactorConfig, new DefaultThreadFactory("httpclient-main", true), new DefaultThreadFactory("httpclient-dispatch", true), dnsResolver, tlsStrategy); } /** * Creates {@link MinimalH2AsyncClient} instance optimized for HTTP/2 multiplexing message * transport without advanced HTTP protocol functionality. */ public static MinimalH2AsyncClient createHttp2Minimal( final H2Config h2Config, final IOReactorConfig ioReactorConfig, final DnsResolver dnsResolver, final TlsStrategy tlsStrategy) { final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry(); return createMinimalHttp2AsyncClientImpl( new H2AsyncClientProtocolStarter( createMinimalProtocolProcessor(), (request, context) -> pushConsumerRegistry.get(request), h2Config, CharCodingConfig.DEFAULT), pushConsumerRegistry, ioReactorConfig, dnsResolver, tlsStrategy); } /** * Creates {@link MinimalH2AsyncClient} instance optimized for HTTP/2 multiplexing message * transport without advanced HTTP protocol functionality. */ public static MinimalH2AsyncClient createHttp2Minimal( final H2Config h2Config, final IOReactorConfig ioReactorConfig, final TlsStrategy tlsStrategy) { return createHttp2Minimal(h2Config, ioReactorConfig, SystemDefaultDnsResolver.INSTANCE, tlsStrategy); } /** * Creates {@link MinimalH2AsyncClient} instance optimized for HTTP/2 multiplexing message * transport without advanced HTTP protocol functionality. */ public static MinimalH2AsyncClient createHttp2Minimal( final H2Config h2Config, final IOReactorConfig ioReactorConfig) { return createHttp2Minimal(h2Config, ioReactorConfig, DefaultClientTlsStrategy.getDefault()); } /** * Creates {@link MinimalH2AsyncClient} instance optimized for HTTP/2 multiplexing message * transport without advanced HTTP protocol functionality. */ public static MinimalH2AsyncClient createHttp2Minimal(final H2Config h2Config) { return createHttp2Minimal(h2Config, IOReactorConfig.DEFAULT); } /** * Creates {@link MinimalH2AsyncClient} instance optimized for HTTP/2 multiplexing message * transport without advanced HTTP protocol functionality. */ public static MinimalH2AsyncClient createHttp2Minimal() { return createHttp2Minimal(H2Config.DEFAULT); } } HttpAsyncMainClientExec.java000066400000000000000000000263451434266521000402160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Usually the last HTTP/1.1 request execution handler in the asynchronous * request execution chain that is responsible for execution of * request/response exchanges with the opposite endpoint. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal class HttpAsyncMainClientExec implements AsyncExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(HttpAsyncMainClientExec.class); private final HttpProcessor httpProcessor; private final ConnectionKeepAliveStrategy keepAliveStrategy; private final UserTokenHandler userTokenHandler; HttpAsyncMainClientExec(final HttpProcessor httpProcessor, final ConnectionKeepAliveStrategy keepAliveStrategy, final UserTokenHandler userTokenHandler) { this.httpProcessor = Args.notNull(httpProcessor, "HTTP protocol processor"); this.keepAliveStrategy = keepAliveStrategy; this.userTokenHandler = userTokenHandler; } @Override public void execute( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final CancellableDependency operation = scope.cancellableDependency; final HttpClientContext clientContext = scope.clientContext; final AsyncExecRuntime execRuntime = scope.execRuntime; if (LOG.isDebugEnabled()) { LOG.debug("{} executing {}", exchangeId, new RequestLine(request)); } final AtomicInteger messageCountDown = new AtomicInteger(2); final AsyncClientExchangeHandler internalExchangeHandler = new AsyncClientExchangeHandler() { private final AtomicReference entityConsumerRef = new AtomicReference<>(); @Override public void releaseResources() { final AsyncDataConsumer entityConsumer = entityConsumerRef.getAndSet(null); if (entityConsumer != null) { entityConsumer.releaseResources(); } } @Override public void failed(final Exception cause) { final AsyncDataConsumer entityConsumer = entityConsumerRef.getAndSet(null); if (entityConsumer != null) { entityConsumer.releaseResources(); } execRuntime.markConnectionNonReusable(); asyncExecCallback.failed(cause); } @Override public void cancel() { if (messageCountDown.get() > 0) { failed(new InterruptedIOException()); } } @Override public void produceRequest( final RequestChannel channel, final HttpContext context) throws HttpException, IOException { clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, route); clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request); httpProcessor.process(request, entityProducer, clientContext); channel.sendRequest(request, entityProducer, context); if (entityProducer == null) { messageCountDown.decrementAndGet(); } } @Override public int available() { return entityProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { entityProducer.produce(new DataStreamChannel() { @Override public void requestOutput() { channel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { return channel.write(src); } @Override public void endStream(final List trailers) throws IOException { channel.endStream(trailers); if (messageCountDown.decrementAndGet() <= 0) { asyncExecCallback.completed(); } } @Override public void endStream() throws IOException { channel.endStream(); if (messageCountDown.decrementAndGet() <= 0) { asyncExecCallback.completed(); } } }); } @Override public void consumeInformation( final HttpResponse response, final HttpContext context) throws HttpException, IOException { asyncExecCallback.handleInformationResponse(response); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, entityDetails, clientContext); entityConsumerRef.set(asyncExecCallback.handleResponse(response, entityDetails)); if (response.getCode() >= HttpStatus.SC_CLIENT_ERROR) { messageCountDown.decrementAndGet(); } final TimeValue keepAliveDuration = keepAliveStrategy.getKeepAliveDuration(response, clientContext); Object userToken = clientContext.getUserToken(); if (userToken == null) { userToken = userTokenHandler.getUserToken(route, request, clientContext); clientContext.setAttribute(HttpClientContext.USER_TOKEN, userToken); } execRuntime.markConnectionReusable(userToken, keepAliveDuration); if (entityDetails == null) { execRuntime.validateConnection(); if (messageCountDown.decrementAndGet() <= 0) { asyncExecCallback.completed(); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { final AsyncDataConsumer entityConsumer = entityConsumerRef.get(); if (entityConsumer != null) { entityConsumer.updateCapacity(capacityChannel); } else { capacityChannel.update(Integer.MAX_VALUE); } } @Override public void consume(final ByteBuffer src) throws IOException { final AsyncDataConsumer entityConsumer = entityConsumerRef.get(); if (entityConsumer != null) { entityConsumer.consume(src); } } @Override public void streamEnd(final List trailers) throws HttpException, IOException { final AsyncDataConsumer entityConsumer = entityConsumerRef.getAndSet(null); if (entityConsumer != null) { entityConsumer.streamEnd(trailers); } else { execRuntime.validateConnection(); } if (messageCountDown.decrementAndGet() <= 0) { asyncExecCallback.completed(); } } }; if (LOG.isDebugEnabled()) { operation.setDependency(execRuntime.execute( exchangeId, new LoggingAsyncClientExchangeHandler(LOG, exchangeId, internalExchangeHandler), clientContext)); } else { operation.setDependency(execRuntime.execute(exchangeId, internalExchangeHandler, clientContext)); } } } InternalAbstractHttpAsyncClient.java000066400000000000000000000436211434266521000417610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.Closeable; import java.io.IOException; import java.util.List; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.util.TimeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBase { private final static ThreadFactory SCHEDULER_THREAD_FACTORY = new DefaultThreadFactory("Scheduled-executor", true); private static final Logger LOG = LoggerFactory.getLogger(InternalAbstractHttpAsyncClient.class); private final AsyncExecChainElement execChain; private final Lookup cookieSpecRegistry; private final Lookup authSchemeRegistry; private final CookieStore cookieStore; private final CredentialsProvider credentialsProvider; private final RequestConfig defaultConfig; private final ConcurrentLinkedQueue closeables; private final ScheduledExecutorService scheduledExecutorService; InternalAbstractHttpAsyncClient( final DefaultConnectingIOReactor ioReactor, final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final AsyncExecChainElement execChain, final Lookup cookieSpecRegistry, final Lookup authSchemeRegistry, final CookieStore cookieStore, final CredentialsProvider credentialsProvider, final RequestConfig defaultConfig, final List closeables) { super(ioReactor, pushConsumerRegistry, threadFactory); this.execChain = execChain; this.cookieSpecRegistry = cookieSpecRegistry; this.authSchemeRegistry = authSchemeRegistry; this.cookieStore = cookieStore; this.credentialsProvider = credentialsProvider; this.defaultConfig = defaultConfig; this.closeables = closeables != null ? new ConcurrentLinkedQueue<>(closeables) : null; this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(SCHEDULER_THREAD_FACTORY); } @Override void internalClose(final CloseMode closeMode) { if (this.closeables != null) { Closeable closeable; while ((closeable = this.closeables.poll()) != null) { try { if (closeable instanceof ModalCloseable) { ((ModalCloseable) closeable).close(closeMode); } else { closeable.close(); } } catch (final IOException ex) { LOG.error(ex.getMessage(), ex); } } } final List runnables = this.scheduledExecutorService.shutdownNow(); for (final Runnable runnable: runnables) { if (runnable instanceof Cancellable) { ((Cancellable) runnable).cancel(); } } } private void setupContext(final HttpClientContext context) { if (context.getAttribute(HttpClientContext.AUTHSCHEME_REGISTRY) == null) { context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry); } if (context.getAttribute(HttpClientContext.COOKIESPEC_REGISTRY) == null) { context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, cookieSpecRegistry); } if (context.getAttribute(HttpClientContext.COOKIE_STORE) == null) { context.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore); } if (context.getAttribute(HttpClientContext.CREDS_PROVIDER) == null) { context.setAttribute(HttpClientContext.CREDS_PROVIDER, credentialsProvider); } if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) { context.setAttribute(HttpClientContext.REQUEST_CONFIG, defaultConfig); } } abstract AsyncExecRuntime createAsyncExecRuntime(HandlerFactory pushHandlerFactory); abstract HttpRoute determineRoute(HttpHost httpHost, HttpClientContext clientContext) throws HttpException; @Override protected Future doExecute( final HttpHost httpHost, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback) { final ComplexFuture future = new ComplexFuture<>(callback); try { if (!isRunning()) { throw new CancellationException("Request execution cancelled"); } final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : HttpClientContext.create(); requestProducer.sendRequest((request, entityDetails, c) -> { RequestConfig requestConfig = null; if (request instanceof Configurable) { requestConfig = ((Configurable) request).getConfig(); } if (requestConfig != null) { clientContext.setRequestConfig(requestConfig); } setupContext(clientContext); final HttpRoute route = determineRoute( httpHost != null ? httpHost : RoutingSupport.determineHost(request), clientContext); final String exchangeId = ExecSupport.getNextExchangeId(); clientContext.setExchangeId(exchangeId); if (LOG.isDebugEnabled()) { LOG.debug("{} preparing request execution", exchangeId); } final AsyncExecRuntime execRuntime = createAsyncExecRuntime(pushHandlerFactory); final AsyncExecChain.Scheduler scheduler = this::executeScheduled; final AsyncExecChain.Scope scope = new AsyncExecChain.Scope(exchangeId, route, request, future, clientContext, execRuntime, scheduler, new AtomicInteger(1)); final AtomicBoolean outputTerminated = new AtomicBoolean(false); executeImmediate( BasicRequestBuilder.copy(request).build(), entityDetails != null ? new AsyncEntityProducer() { @Override public void releaseResources() { requestProducer.releaseResources(); } @Override public void failed(final Exception cause) { requestProducer.failed(cause); } @Override public boolean isRepeatable() { return requestProducer.isRepeatable(); } @Override public long getContentLength() { return entityDetails.getContentLength(); } @Override public String getContentType() { return entityDetails.getContentType(); } @Override public String getContentEncoding() { return entityDetails.getContentEncoding(); } @Override public boolean isChunked() { return entityDetails.isChunked(); } @Override public Set getTrailerNames() { return entityDetails.getTrailerNames(); } @Override public int available() { return requestProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { if (outputTerminated.get()) { channel.endStream(); return; } requestProducer.produce(channel); } } : null, scope, new AsyncExecCallback() { @Override public AsyncDataConsumer handleResponse( final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException { if (response.getCode() >= HttpStatus.SC_CLIENT_ERROR) { outputTerminated.set(true); requestProducer.releaseResources(); } responseConsumer.consumeResponse(response, entityDetails, c, new FutureCallback() { @Override public void completed(final T result) { future.completed(result); } @Override public void failed(final Exception ex) { future.failed(ex); } @Override public void cancelled() { future.cancel(); } }); return entityDetails != null ? responseConsumer : null; } @Override public void handleInformationResponse( final HttpResponse response) throws HttpException, IOException { responseConsumer.informationResponse(response, c); } @Override public void completed() { if (LOG.isDebugEnabled()) { LOG.debug("{} message exchange successfully completed", exchangeId); } try { execRuntime.releaseEndpoint(); } finally { responseConsumer.releaseResources(); requestProducer.releaseResources(); } } @Override public void failed(final Exception cause) { if (LOG.isDebugEnabled()) { LOG.debug("{} request failed: {}", exchangeId, cause.getMessage()); } try { execRuntime.discardEndpoint(); responseConsumer.failed(cause); } finally { try { future.failed(cause); } finally { responseConsumer.releaseResources(); requestProducer.releaseResources(); } } } }); }, context); } catch (final HttpException | IOException | IllegalStateException ex) { future.failed(ex); } return future; } void executeImmediate( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { execChain.execute(request, entityProducer, scope, asyncExecCallback); } void executeScheduled( final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecCallback asyncExecCallback, final TimeValue delay) { final ScheduledRequestExecution scheduledTask = new ScheduledRequestExecution( request, entityProducer, scope, asyncExecCallback, delay); if (TimeValue.isPositive(delay)) { scheduledExecutorService.schedule(scheduledTask, delay.getDuration(), delay.getTimeUnit()); } else { scheduledExecutorService.execute(scheduledTask); } } class ScheduledRequestExecution implements Runnable, Cancellable { final HttpRequest request; final AsyncEntityProducer entityProducer; final AsyncExecChain.Scope scope; final AsyncExecCallback asyncExecCallback; final TimeValue delay; ScheduledRequestExecution(final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecCallback asyncExecCallback, final TimeValue delay) { this.request = request; this.entityProducer = entityProducer; this.scope = scope; this.asyncExecCallback = asyncExecCallback; this.delay = delay; } @Override public void run() { try { execChain.execute(request, entityProducer, scope, asyncExecCallback); } catch (final Exception ex) { asyncExecCallback.failed(ex); } } @Override public boolean cancel() { asyncExecCallback.failed(new CancellationException("Request execution cancelled")); return true; } } } InternalH2AsyncClient.java000066400000000000000000000106201434266521000376200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.Closeable; import java.util.List; import java.util.concurrent.ThreadFactory; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Internal implementation of HTTP/2 only {@link CloseableHttpAsyncClient}. *

* Concurrent message exchanges with the same connection route executed by * this client will get automatically multiplexed over a single physical HTTP/2 * connection. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) @Internal public final class InternalH2AsyncClient extends InternalAbstractHttpAsyncClient { private static final Logger LOG = LoggerFactory.getLogger(InternalH2AsyncClient.class); private final HttpRoutePlanner routePlanner; private final InternalH2ConnPool connPool; InternalH2AsyncClient( final DefaultConnectingIOReactor ioReactor, final AsyncExecChainElement execChain, final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final InternalH2ConnPool connPool, final HttpRoutePlanner routePlanner, final Lookup cookieSpecRegistry, final Lookup authSchemeRegistry, final CookieStore cookieStore, final CredentialsProvider credentialsProvider, final RequestConfig defaultConfig, final List closeables) { super(ioReactor, pushConsumerRegistry, threadFactory, execChain, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, defaultConfig, closeables); this.connPool = connPool; this.routePlanner = routePlanner; } @Override AsyncExecRuntime createAsyncExecRuntime(final HandlerFactory pushHandlerFactory) { return new InternalH2AsyncExecRuntime(LOG, connPool, pushHandlerFactory); } @Override HttpRoute determineRoute(final HttpHost httpHost, final HttpClientContext clientContext) throws HttpException { final HttpRoute route = routePlanner.determineRoute(httpHost, clientContext); if (route.isTunnelled()) { throw new HttpException("HTTP/2 tunneling not supported"); } return route; } } InternalH2AsyncExecRuntime.java000066400000000000000000000260051434266521000406360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.InterruptedIOException; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.ComplexCancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; class InternalH2AsyncExecRuntime implements AsyncExecRuntime { private final Logger log; private final InternalH2ConnPool connPool; private final HandlerFactory pushHandlerFactory; private final AtomicReference sessionRef; private volatile boolean reusable; InternalH2AsyncExecRuntime( final Logger log, final InternalH2ConnPool connPool, final HandlerFactory pushHandlerFactory) { super(); this.log = log; this.connPool = connPool; this.pushHandlerFactory = pushHandlerFactory; this.sessionRef = new AtomicReference<>(); } @Override public boolean isEndpointAcquired() { return sessionRef.get() != null; } @Override public Cancellable acquireEndpoint( final String id, final HttpRoute route, final Object object, final HttpClientContext context, final FutureCallback callback) { if (sessionRef.get() == null) { final HttpHost target = route.getTargetHost(); final RequestConfig requestConfig = context.getRequestConfig(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); if (log.isDebugEnabled()) { log.debug("{} acquiring endpoint ({})", id, connectTimeout); } return Operations.cancellable(connPool.getSession(target, connectTimeout, new FutureCallback() { @Override public void completed(final IOSession ioSession) { sessionRef.set(new Endpoint(target, ioSession)); reusable = true; if (log.isDebugEnabled()) { log.debug("{} acquired endpoint", id); } callback.completed(InternalH2AsyncExecRuntime.this); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } })); } callback.completed(this); return Operations.nonCancellable(); } private void closeEndpoint(final Endpoint endpoint) { endpoint.session.close(CloseMode.GRACEFUL); if (log.isDebugEnabled()) { log.debug("{} endpoint closed", ConnPoolSupport.getId(endpoint)); } } @Override public void releaseEndpoint() { final Endpoint endpoint = sessionRef.getAndSet(null); if (endpoint != null && !reusable) { closeEndpoint(endpoint); } } @Override public void discardEndpoint() { final Endpoint endpoint = sessionRef.getAndSet(null); if (endpoint != null) { closeEndpoint(endpoint); } } @Override public boolean validateConnection() { if (reusable) { final Endpoint endpoint = sessionRef.get(); return endpoint != null && endpoint.session.isOpen(); } final Endpoint endpoint = sessionRef.getAndSet(null); if (endpoint != null) { closeEndpoint(endpoint); } return false; } @Override public boolean isEndpointConnected() { final Endpoint endpoint = sessionRef.get(); return endpoint != null && endpoint.session.isOpen(); } Endpoint ensureValid() { final Endpoint endpoint = sessionRef.get(); if (endpoint == null) { throw new IllegalStateException("I/O session not acquired / already released"); } return endpoint; } @Override public Cancellable connectEndpoint( final HttpClientContext context, final FutureCallback callback) { final Endpoint endpoint = ensureValid(); if (endpoint.session.isOpen()) { callback.completed(this); return Operations.nonCancellable(); } final HttpHost target = endpoint.target; final RequestConfig requestConfig = context.getRequestConfig(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); if (log.isDebugEnabled()) { log.debug("{} connecting endpoint ({})", ConnPoolSupport.getId(endpoint), connectTimeout); } return Operations.cancellable(connPool.getSession(target, connectTimeout, new FutureCallback() { @Override public void completed(final IOSession ioSession) { sessionRef.set(new Endpoint(target, ioSession)); reusable = true; if (log.isDebugEnabled()) { log.debug("{} endpoint connected", ConnPoolSupport.getId(endpoint)); } callback.completed(InternalH2AsyncExecRuntime.this); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } })); } @Override public void upgradeTls(final HttpClientContext context) { throw new UnsupportedOperationException(); } @Override public void upgradeTls(final HttpClientContext context, final FutureCallback callback) { throw new UnsupportedOperationException(); } @Override public Cancellable execute( final String id, final AsyncClientExchangeHandler exchangeHandler, final HttpClientContext context) { final ComplexCancellable complexCancellable = new ComplexCancellable(); final Endpoint endpoint = ensureValid(); final IOSession session = endpoint.session; if (session.isOpen()) { if (log.isDebugEnabled()) { log.debug("{} start execution {}", ConnPoolSupport.getId(endpoint), id); } context.setProtocolVersion(HttpVersion.HTTP_2); session.enqueue( new RequestExecutionCommand(exchangeHandler, pushHandlerFactory, complexCancellable, context), Command.Priority.NORMAL); } else { final HttpHost target = endpoint.target; final RequestConfig requestConfig = context.getRequestConfig(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); connPool.getSession(target, connectTimeout, new FutureCallback() { @Override public void completed(final IOSession ioSession) { sessionRef.set(new Endpoint(target, ioSession)); reusable = true; if (log.isDebugEnabled()) { log.debug("{} start execution {}", ConnPoolSupport.getId(endpoint), id); } context.setProtocolVersion(HttpVersion.HTTP_2); session.enqueue( new RequestExecutionCommand(exchangeHandler, pushHandlerFactory, complexCancellable, context), Command.Priority.NORMAL); } @Override public void failed(final Exception ex) { exchangeHandler.failed(ex); } @Override public void cancelled() { exchangeHandler.failed(new InterruptedIOException()); } }); } return complexCancellable; } @Override public void markConnectionReusable(final Object newState, final TimeValue newValidDuration) { throw new UnsupportedOperationException(); } @Override public void markConnectionNonReusable() { reusable = false; } static class Endpoint implements Identifiable { final HttpHost target; final IOSession session; Endpoint(final HttpHost target, final IOSession session) { this.target = target; this.session = session; } @Override public String getId() { return session.getId(); } } @Override public AsyncExecRuntime fork() { return new InternalH2AsyncExecRuntime(log, connPool, pushHandlerFactory); } } InternalH2ConnPool.java000066400000000000000000000101541434266521000371350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.net.InetSocketAddress; import java.util.concurrent.Future; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http2.nio.pool.H2ConnPool; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; class InternalH2ConnPool implements ModalCloseable { private final H2ConnPool connPool; private volatile Resolver connectionConfigResolver; InternalH2ConnPool(final ConnectionInitiator connectionInitiator, final Resolver addressResolver, final TlsStrategy tlsStrategy) { this.connPool = new H2ConnPool(connectionInitiator, addressResolver, tlsStrategy); } @Override public void close(final CloseMode closeMode) { connPool.close(closeMode); } @Override public void close() { connPool.close(); } private ConnectionConfig resolveConnectionConfig(final HttpHost httpHost) { final Resolver resolver = this.connectionConfigResolver; final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(httpHost) : null; return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT; } public Future getSession( final HttpHost endpoint, final Timeout connectTimeout, final FutureCallback callback) { final ConnectionConfig connectionConfig = resolveConnectionConfig(endpoint); return connPool.getSession( endpoint, connectTimeout != null ? connectTimeout : connectionConfig.getConnectTimeout(), new CallbackContribution(callback) { @Override public void completed(final IOSession ioSession) { final Timeout socketTimeout = connectionConfig.getSocketTimeout(); if (socketTimeout != null) { ioSession.setSocketTimeout(socketTimeout); } callback.completed(ioSession); } }); } public void closeIdle(final TimeValue idleTime) { connPool.closeIdle(idleTime); } public void setConnectionConfigResolver(final Resolver connectionConfigResolver) { this.connectionConfigResolver = connectionConfigResolver; } } InternalHttpAsyncClient.java000066400000000000000000000112201434266521000402630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.Closeable; import java.util.List; import java.util.concurrent.ThreadFactory; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.nio.AsyncClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Internal implementation of {@link CloseableHttpAsyncClient} that can negotiate * the most optimal HTTP protocol version during during the {@code TLS} handshake * with {@code ALPN} extension if supported by the Java runtime. *

* Concurrent message exchanges executed by this client will get assigned to * separate connections leased from the connection pool. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) @Internal public final class InternalHttpAsyncClient extends InternalAbstractHttpAsyncClient { private static final Logger LOG = LoggerFactory.getLogger(InternalHttpAsyncClient.class); private final AsyncClientConnectionManager manager; private final HttpRoutePlanner routePlanner; private final TlsConfig tlsConfig; InternalHttpAsyncClient( final DefaultConnectingIOReactor ioReactor, final AsyncExecChainElement execChain, final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final AsyncClientConnectionManager manager, final HttpRoutePlanner routePlanner, final TlsConfig tlsConfig, final Lookup cookieSpecRegistry, final Lookup authSchemeRegistry, final CookieStore cookieStore, final CredentialsProvider credentialsProvider, final RequestConfig defaultConfig, final List closeables) { super(ioReactor, pushConsumerRegistry, threadFactory, execChain, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, defaultConfig, closeables); this.manager = manager; this.routePlanner = routePlanner; this.tlsConfig = tlsConfig; } @Override AsyncExecRuntime createAsyncExecRuntime(final HandlerFactory pushHandlerFactory) { return new InternalHttpAsyncExecRuntime(LOG, manager, getConnectionInitiator(), pushHandlerFactory, tlsConfig); } @Override HttpRoute determineRoute(final HttpHost httpHost, final HttpClientContext clientContext) throws HttpException { return routePlanner.determineRoute(httpHost, clientContext); } } InternalHttpAsyncExecRuntime.java000066400000000000000000000304201434266521000413000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.InterruptedIOException; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.Operations; import org.apache.hc.client5.http.nio.AsyncClientConnectionManager; import org.apache.hc.client5.http.nio.AsyncConnectionEndpoint; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; class InternalHttpAsyncExecRuntime implements AsyncExecRuntime { private final Logger log; private final AsyncClientConnectionManager manager; private final ConnectionInitiator connectionInitiator; private final HandlerFactory pushHandlerFactory; /** * @deprecated TLS should be configured by the connection manager */ @Deprecated private final TlsConfig tlsConfig; private final AtomicReference endpointRef; private volatile boolean reusable; private volatile Object state; private volatile TimeValue validDuration; InternalHttpAsyncExecRuntime( final Logger log, final AsyncClientConnectionManager manager, final ConnectionInitiator connectionInitiator, final HandlerFactory pushHandlerFactory, final TlsConfig tlsConfig) { super(); this.log = log; this.manager = manager; this.connectionInitiator = connectionInitiator; this.pushHandlerFactory = pushHandlerFactory; this.tlsConfig = tlsConfig; this.endpointRef = new AtomicReference<>(); this.validDuration = TimeValue.NEG_ONE_MILLISECOND; } @Override public boolean isEndpointAcquired() { return endpointRef.get() != null; } @Override public Cancellable acquireEndpoint( final String id, final HttpRoute route, final Object object, final HttpClientContext context, final FutureCallback callback) { if (endpointRef.get() == null) { state = object; final RequestConfig requestConfig = context.getRequestConfig(); final Timeout connectionRequestTimeout = requestConfig.getConnectionRequestTimeout(); if (log.isDebugEnabled()) { log.debug("{} acquiring endpoint ({})", id, connectionRequestTimeout); } return Operations.cancellable(manager.lease( id, route, object, connectionRequestTimeout, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint connectionEndpoint) { endpointRef.set(connectionEndpoint); reusable = connectionEndpoint.isConnected(); if (log.isDebugEnabled()) { log.debug("{} acquired endpoint {}", id, ConnPoolSupport.getId(connectionEndpoint)); } callback.completed(InternalHttpAsyncExecRuntime.this); } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } })); } callback.completed(this); return Operations.nonCancellable(); } private void discardEndpoint(final AsyncConnectionEndpoint endpoint) { try { endpoint.close(CloseMode.IMMEDIATE); if (log.isDebugEnabled()) { log.debug("{} endpoint closed", ConnPoolSupport.getId(endpoint)); } } finally { if (log.isDebugEnabled()) { log.debug("{} discarding endpoint", ConnPoolSupport.getId(endpoint)); } manager.release(endpoint, null, TimeValue.ZERO_MILLISECONDS); } } @Override public void releaseEndpoint() { final AsyncConnectionEndpoint endpoint = endpointRef.getAndSet(null); if (endpoint != null) { if (reusable) { if (log.isDebugEnabled()) { log.debug("{} releasing valid endpoint", ConnPoolSupport.getId(endpoint)); } manager.release(endpoint, state, validDuration); } else { discardEndpoint(endpoint); } } } @Override public void discardEndpoint() { final AsyncConnectionEndpoint endpoint = endpointRef.getAndSet(null); if (endpoint != null) { discardEndpoint(endpoint); } } @Override public boolean validateConnection() { if (reusable) { final AsyncConnectionEndpoint endpoint = endpointRef.get(); return endpoint != null && endpoint.isConnected(); } final AsyncConnectionEndpoint endpoint = endpointRef.getAndSet(null); if (endpoint != null) { discardEndpoint(endpoint); } return false; } AsyncConnectionEndpoint ensureValid() { final AsyncConnectionEndpoint endpoint = endpointRef.get(); if (endpoint == null) { throw new IllegalStateException("Endpoint not acquired / already released"); } return endpoint; } @Override public boolean isEndpointConnected() { final AsyncConnectionEndpoint endpoint = endpointRef.get(); return endpoint != null && endpoint.isConnected(); } @Override public Cancellable connectEndpoint( final HttpClientContext context, final FutureCallback callback) { final AsyncConnectionEndpoint endpoint = ensureValid(); if (endpoint.isConnected()) { callback.completed(this); return Operations.nonCancellable(); } final RequestConfig requestConfig = context.getRequestConfig(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); if (log.isDebugEnabled()) { log.debug("{} connecting endpoint ({})", ConnPoolSupport.getId(endpoint), connectTimeout); } return Operations.cancellable(manager.connect( endpoint, connectionInitiator, connectTimeout, tlsConfig, context, new CallbackContribution(callback) { @Override public void completed(final AsyncConnectionEndpoint endpoint) { if (log.isDebugEnabled()) { log.debug("{} endpoint connected", ConnPoolSupport.getId(endpoint)); } if (callback != null) { callback.completed(InternalHttpAsyncExecRuntime.this); } } })); } @Override public void upgradeTls(final HttpClientContext context) { upgradeTls(context, null); } @Override public void upgradeTls(final HttpClientContext context, final FutureCallback callback) { final AsyncConnectionEndpoint endpoint = ensureValid(); if (log.isDebugEnabled()) { log.debug("{} upgrading endpoint", ConnPoolSupport.getId(endpoint)); } manager.upgrade(endpoint, tlsConfig, context, new CallbackContribution(callback) { @Override public void completed(final AsyncConnectionEndpoint endpoint) { if (callback != null) { callback.completed(InternalHttpAsyncExecRuntime.this); } } }); } @Override public Cancellable execute( final String id, final AsyncClientExchangeHandler exchangeHandler, final HttpClientContext context) { final AsyncConnectionEndpoint endpoint = ensureValid(); if (endpoint.isConnected()) { if (log.isDebugEnabled()) { log.debug("{} start execution {}", ConnPoolSupport.getId(endpoint), id); } final RequestConfig requestConfig = context.getRequestConfig(); final Timeout responseTimeout = requestConfig.getResponseTimeout(); if (responseTimeout != null) { endpoint.setSocketTimeout(responseTimeout); } endpoint.execute(id, exchangeHandler, context); if (context.getRequestConfig().isHardCancellationEnabled()) { return () -> { exchangeHandler.cancel(); return true; }; } } else { connectEndpoint(context, new FutureCallback() { @Override public void completed(final AsyncExecRuntime runtime) { if (log.isDebugEnabled()) { log.debug("{} start execution {}", ConnPoolSupport.getId(endpoint), id); } try { endpoint.execute(id, exchangeHandler, pushHandlerFactory, context); } catch (final RuntimeException ex) { failed(ex); } } @Override public void failed(final Exception ex) { exchangeHandler.failed(ex); } @Override public void cancelled() { exchangeHandler.failed(new InterruptedIOException()); } }); } return Operations.nonCancellable(); } @Override public void markConnectionReusable(final Object newState, final TimeValue newValidDuration) { reusable = true; state = newState; validDuration = newValidDuration; } @Override public void markConnectionNonReusable() { reusable = false; state = null; validDuration = null; } @Override public AsyncExecRuntime fork() { return new InternalHttpAsyncExecRuntime(log, manager, connectionInitiator, pushHandlerFactory, tlsConfig); } } LogAppendable.java000066400000000000000000000046151434266521000362210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import org.slf4j.Logger; final class LogAppendable implements Appendable { private final Logger log; private final String prefix; private final StringBuilder buffer; public LogAppendable(final Logger log, final String prefix) { this.log = log; this.prefix = prefix; this.buffer = new StringBuilder(); } @Override public Appendable append(final CharSequence text) throws IOException { return append(text, 0, text.length()); } @Override public Appendable append(final CharSequence text, final int start, final int end) throws IOException { for (int i = start; i < end; i++) { append(text.charAt(i)); } return this; } @Override public Appendable append(final char ch) throws IOException { if (ch == '\n') { log.debug("{} {}", prefix, buffer); buffer.setLength(0); } else if (ch != '\r') { buffer.append(ch); } return this; } public void flush() { if (buffer.length() > 0) { log.debug("{} {}", prefix, buffer); buffer.setLength(0); } } } LoggingAsyncClientExchangeHandler.java000066400000000000000000000150031434266521000422010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Identifiable; import org.slf4j.Logger; final class LoggingAsyncClientExchangeHandler implements AsyncClientExchangeHandler, Identifiable { private final Logger log; private final String exchangeId; private final AsyncClientExchangeHandler handler; LoggingAsyncClientExchangeHandler(final Logger log, final String exchangeId, final AsyncClientExchangeHandler handler) { this.log = log; this.exchangeId = exchangeId; this.handler = handler; } @Override public String getId() { return exchangeId; } @Override public void releaseResources() { handler.releaseResources(); } @Override public void produceRequest(final RequestChannel channel, final HttpContext context) throws HttpException, IOException { handler.produceRequest((request, entityDetails, context1) -> { if (log.isDebugEnabled()) { log.debug("{} send request {}, {}", exchangeId, new RequestLine(request), entityDetails != null ? "entity len " + entityDetails.getContentLength() : "null entity"); } channel.sendRequest(request, entityDetails, context1); }, context); } @Override public int available() { return handler.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { if (log.isDebugEnabled()) { log.debug("{}: produce request data", exchangeId); } handler.produce(new DataStreamChannel() { @Override public void requestOutput() { channel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { if (log.isDebugEnabled()) { log.debug("{}: produce request data, len {} bytes", exchangeId, src.remaining()); } return channel.write(src); } @Override public void endStream() throws IOException { if (log.isDebugEnabled()) { log.debug("{}: end of request data", exchangeId); } channel.endStream(); } @Override public void endStream(final List trailers) throws IOException { if (log.isDebugEnabled()) { log.debug("{}: end of request data", exchangeId); } channel.endStream(trailers); } }); } @Override public void consumeInformation( final HttpResponse response, final HttpContext context) throws HttpException, IOException { if (log.isDebugEnabled()) { log.debug("{}: information response {}", exchangeId, new StatusLine(response)); } handler.consumeInformation(response, context); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { if (log.isDebugEnabled()) { log.debug("{}: consume response {}, {}", exchangeId, new StatusLine(response), entityDetails != null ? "entity len " + entityDetails.getContentLength() : " null entity"); } handler.consumeResponse(response, entityDetails, context); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { handler.updateCapacity(increment -> { if (log.isDebugEnabled()) { log.debug("{} capacity update {}", exchangeId, increment); } capacityChannel.update(increment); }); } @Override public void consume(final ByteBuffer src) throws IOException { if (log.isDebugEnabled()) { log.debug("{}: consume response data, len {} bytes", exchangeId, src.remaining()); } handler.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { if (log.isDebugEnabled()) { log.debug("{}: end of response data", exchangeId); } handler.streamEnd(trailers); } @Override public void failed(final Exception cause) { if (log.isDebugEnabled()) { log.debug("{}: execution failed: {}", exchangeId, cause.getMessage()); } handler.failed(cause); } @Override public void cancel() { if (log.isDebugEnabled()) { log.debug("{}: execution cancelled", exchangeId); } handler.cancel(); } } LoggingExceptionCallback.java000066400000000000000000000033261434266521000404040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import org.apache.hc.core5.function.Callback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class LoggingExceptionCallback implements Callback { static final LoggingExceptionCallback INSTANCE = new LoggingExceptionCallback(); private static final Logger LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.impl.async"); private LoggingExceptionCallback() { } @Override public void execute(final Exception ex) { LOG.error(ex.getMessage(), ex); } } LoggingIOSession.java000066400000000000000000000232111434266521000366770ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.SelectionKey; import java.util.concurrent.locks.Lock; import org.apache.hc.core5.http.Chars; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; class LoggingIOSession implements IOSession { private final Logger log; private final Logger wireLog; private final IOSession session; public LoggingIOSession(final IOSession session, final Logger log, final Logger wireLog) { super(); this.session = session; this.log = log; this.wireLog = wireLog; } @Override public String getId() { return session.getId(); } @Override public Lock getLock() { return session.getLock(); } @Override public boolean hasCommands() { return session.hasCommands(); } @Override public Command poll() { return session.poll(); } @Override public void enqueue(final Command command, final Command.Priority priority) { session.enqueue(command, priority); if (log.isDebugEnabled()) { log.debug("{} Enqueued {} with priority {}", session, command.getClass().getSimpleName(), priority); } } @Override public ByteChannel channel() { return session.channel(); } @Override public SocketAddress getLocalAddress() { return session.getLocalAddress(); } @Override public SocketAddress getRemoteAddress() { return session.getRemoteAddress(); } @Override public int getEventMask() { return 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) { session.setEventMask(ops); if (log.isDebugEnabled()) { log.debug("{} Event mask set {}", session, formatOps(ops)); } } @Override public void setEvent(final int op) { session.setEvent(op); if (log.isDebugEnabled()) { log.debug("{} Event set {}", session, formatOps(op)); } } @Override public void clearEvent(final int op) { session.clearEvent(op); if (log.isDebugEnabled()) { log.debug("{} Event cleared {}", session, formatOps(op)); } } @Override public boolean isOpen() { return session.isOpen(); } @Override public void close() { if (log.isDebugEnabled()) { log.debug("{} Close", session); } session.close(); } @Override public Status getStatus() { return session.getStatus(); } @Override public void close(final CloseMode closeMode) { if (log.isDebugEnabled()) { log.debug("{} Close {}", session, closeMode); } session.close(closeMode); } @Override public Timeout getSocketTimeout() { return session.getSocketTimeout(); } @Override public void setSocketTimeout(final Timeout timeout) { if (log.isDebugEnabled()) { log.debug("{} Set timeout {}", session, timeout); } session.setSocketTimeout(timeout); } @Override public long getLastReadTime() { return session.getLastReadTime(); } @Override public long getLastWriteTime() { return session.getLastWriteTime(); } @Override public void updateReadTime() { session.updateReadTime(); } @Override public void updateWriteTime() { session.updateWriteTime(); } @Override public long getLastEventTime() { return session.getLastEventTime(); } @Override public IOEventHandler getHandler() { return session.getHandler(); } @Override public void upgrade(final IOEventHandler handler) { Args.notNull(handler, "Protocol handler"); if (log.isDebugEnabled()) { log.debug("{} protocol upgrade {}", session, handler.getClass()); } session.upgrade(new IOEventHandler() { @Override public void connected(final IOSession protocolSession) throws IOException { handler.connected(protocolSession); } @Override public void inputReady(final IOSession protocolSession, final ByteBuffer src) throws IOException { if (src != null && wireLog.isDebugEnabled()) { final ByteBuffer b = src.duplicate(); logData(b, "<< "); } handler.inputReady(protocolSession, src); } @Override public void outputReady(final IOSession protocolSession) throws IOException { handler.outputReady(protocolSession); } @Override public void timeout(final IOSession protocolSession, final Timeout timeout) throws IOException { handler.timeout(protocolSession, timeout); } @Override public void exception(final IOSession protocolSession, final Exception cause) { handler.exception(protocolSession, cause); } @Override public void disconnected(final IOSession protocolSession) { handler.disconnected(protocolSession); } }); } private void logData(final ByteBuffer data, final String prefix) throws IOException { final byte[] line = new byte[16]; final StringBuilder buf = new StringBuilder(); while (data.hasRemaining()) { buf.setLength(0); buf.append(session).append(" ").append(prefix); final int chunk = Math.min(data.remaining(), line.length); data.get(line, 0, chunk); for (int i = 0; i < chunk; i++) { final char ch = (char) line[i]; if (ch > Chars.SP && ch <= Chars.DEL) { buf.append(ch); } else if (Character.isWhitespace(ch)) { buf.append(' '); } else { buf.append('.'); } } for (int i = chunk; i < 17; i++) { buf.append(' '); } for (int i = 0; i < chunk; i++) { buf.append(' '); final int b = line[i] & 0xff; final String s = Integer.toHexString(b); if (s.length() == 1) { buf.append("0"); } buf.append(s); } wireLog.debug(buf.toString()); } } @Override public int read(final ByteBuffer dst) throws IOException { final int bytesRead = session.read(dst); if (log.isDebugEnabled()) { log.debug("{} {} bytes read", session, bytesRead); } if (bytesRead > 0 && wireLog.isDebugEnabled()) { final ByteBuffer b = dst.duplicate(); final int p = b.position(); b.limit(p); b.position(p - bytesRead); logData(b, "<< "); } return bytesRead; } @Override public int write(final ByteBuffer src) throws IOException { final int byteWritten = session.write(src); if (log.isDebugEnabled()) { log.debug("{} {} bytes written", session, byteWritten); } if (byteWritten > 0 && wireLog.isDebugEnabled()) { final ByteBuffer b = src.duplicate(); final int p = b.position(); b.limit(p); b.position(p - byteWritten); logData(b, ">> "); } return byteWritten; } @Override public String toString() { return session.toString(); } } LoggingIOSessionDecorator.java000066400000000000000000000040261434266521000405450ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.reactor.IOSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; final class LoggingIOSessionDecorator implements Decorator { public final static LoggingIOSessionDecorator INSTANCE = new LoggingIOSessionDecorator(); private static final Logger WIRE_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.wire"); private LoggingIOSessionDecorator() { } @Override public IOSession decorate(final IOSession ioSession) { final Logger sessionLog = LoggerFactory.getLogger(ioSession.getClass()); if (sessionLog.isDebugEnabled() || WIRE_LOG.isDebugEnabled()) { return new LoggingIOSession(ioSession, sessionLog, WIRE_LOG); } else { return ioSession; } } } MinimalH2AsyncClient.java000066400000000000000000000304071434266521000374370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.impl.classic.RequestFailedException; import org.apache.hc.client5.http.impl.nio.MultihomeConnectionInitiator; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.ComplexCancellable; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Minimal implementation of HTTP/2 only {@link CloseableHttpAsyncClient}. This client * is optimized for HTTP/2 multiplexing message transport and does not support advanced * HTTP protocol functionality such as request execution via a proxy, state management, * authentication and request redirects. *

* Concurrent message exchanges with the same connection route executed by * this client will get automatically multiplexed over a single physical HTTP/2 * connection. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public final class MinimalH2AsyncClient extends AbstractMinimalHttpAsyncClientBase { private static final Logger LOG = LoggerFactory.getLogger(MinimalH2AsyncClient.class); private final InternalH2ConnPool connPool; private final ConnectionInitiator connectionInitiator; MinimalH2AsyncClient( final IOEventHandlerFactory eventHandlerFactory, final AsyncPushConsumerRegistry pushConsumerRegistry, final IOReactorConfig reactorConfig, final ThreadFactory threadFactory, final ThreadFactory workerThreadFactory, final DnsResolver dnsResolver, final TlsStrategy tlsStrategy) { super(new DefaultConnectingIOReactor( eventHandlerFactory, reactorConfig, workerThreadFactory, LoggingIOSessionDecorator.INSTANCE, LoggingExceptionCallback.INSTANCE, null, ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.IMMEDIATE)), pushConsumerRegistry, threadFactory); this.connectionInitiator = new MultihomeConnectionInitiator(getConnectionInitiator(), dnsResolver); this.connPool = new InternalH2ConnPool(this.connectionInitiator, object -> null, tlsStrategy); } @Override public Cancellable execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { final ComplexCancellable cancellable = new ComplexCancellable(); try { if (!isRunning()) { throw new CancellationException("Request execution cancelled"); } final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : HttpClientContext.create(); exchangeHandler.produceRequest((request, entityDetails, context1) -> { RequestConfig requestConfig = null; if (request instanceof Configurable) { requestConfig = ((Configurable) request).getConfig(); } if (requestConfig != null) { clientContext.setRequestConfig(requestConfig); } else { requestConfig = clientContext.getRequestConfig(); } @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority()); final Future sessionFuture = connPool.getSession(target, connectTimeout, new FutureCallback() { @Override public void completed(final IOSession session) { final AsyncClientExchangeHandler internalExchangeHandler = new AsyncClientExchangeHandler() { @Override public void releaseResources() { exchangeHandler.releaseResources(); } @Override public void failed(final Exception cause) { exchangeHandler.failed(cause); } @Override public void cancel() { failed(new RequestFailedException("Request aborted")); } @Override public void produceRequest( final RequestChannel channel, final HttpContext context1) throws HttpException, IOException { channel.sendRequest(request, entityDetails, context1); } @Override public int available() { return exchangeHandler.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { exchangeHandler.produce(channel); } @Override public void consumeInformation( final HttpResponse response, final HttpContext context1) throws HttpException, IOException { exchangeHandler.consumeInformation(response, context1); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context1) throws HttpException, IOException { exchangeHandler.consumeResponse(response, entityDetails, context1); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { exchangeHandler.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { exchangeHandler.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { exchangeHandler.streamEnd(trailers); } }; if (LOG.isDebugEnabled()) { final String exchangeId = ExecSupport.getNextExchangeId(); clientContext.setExchangeId(exchangeId); if (LOG.isDebugEnabled()) { LOG.debug("{} executing message exchange {}", exchangeId, ConnPoolSupport.getId(session)); } session.enqueue( new RequestExecutionCommand( new LoggingAsyncClientExchangeHandler(LOG, exchangeId, internalExchangeHandler), pushHandlerFactory, cancellable, clientContext), Command.Priority.NORMAL); } else { session.enqueue( new RequestExecutionCommand( internalExchangeHandler, pushHandlerFactory, cancellable, clientContext), Command.Priority.NORMAL); } } @Override public void failed(final Exception ex) { exchangeHandler.failed(ex); } @Override public void cancelled() { exchangeHandler.cancel(); } }); cancellable.setDependency(() -> sessionFuture.cancel(true)); }, context); } catch (final HttpException | IOException | IllegalStateException ex) { exchangeHandler.failed(ex); } return cancellable; } /** * Sets {@link Resolver} for {@link ConnectionConfig} on a per host basis. * * @since 5.2 */ public void setConnectionConfigResolver(final Resolver connectionConfigResolver) { connPool.setConnectionConfigResolver(connectionConfigResolver); } } MinimalHttpAsyncClient.java000066400000000000000000000561201434266521000401050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.async; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.impl.classic.RequestFailedException; import org.apache.hc.client5.http.nio.AsyncClientConnectionManager; import org.apache.hc.client5.http.nio.AsyncConnectionEndpoint; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.ComplexCancellable; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOEventHandlerFactory; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Minimal implementation of {@link CloseableHttpAsyncClient}. This client is * optimized for HTTP/1.1 and HTTP/2 message transport and does not support * advanced HTTP protocol functionality such as request execution via a proxy, * state management, authentication and request redirects. *

* Concurrent message exchanges executed by this client will get assigned to * separate connections leased from the connection pool. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClientBase { private static final Logger LOG = LoggerFactory.getLogger(MinimalHttpAsyncClient.class); private final AsyncClientConnectionManager manager; private final SchemePortResolver schemePortResolver; private final TlsConfig tlsConfig; MinimalHttpAsyncClient( final IOEventHandlerFactory eventHandlerFactory, final AsyncPushConsumerRegistry pushConsumerRegistry, final IOReactorConfig reactorConfig, final ThreadFactory threadFactory, final ThreadFactory workerThreadFactory, final AsyncClientConnectionManager manager, final SchemePortResolver schemePortResolver, final TlsConfig tlsConfig) { super(new DefaultConnectingIOReactor( eventHandlerFactory, reactorConfig, workerThreadFactory, LoggingIOSessionDecorator.INSTANCE, LoggingExceptionCallback.INSTANCE, null, ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.NORMAL)), pushConsumerRegistry, threadFactory); this.manager = manager; this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; this.tlsConfig = tlsConfig; } private Future leaseEndpoint( final HttpHost host, final Timeout connectionRequestTimeout, final Timeout connectTimeout, final HttpClientContext clientContext, final FutureCallback callback) { final HttpRoute route = new HttpRoute(RoutingSupport.normalize(host, schemePortResolver)); final ComplexFuture resultFuture = new ComplexFuture<>(callback); final String exchangeId = ExecSupport.getNextExchangeId(); clientContext.setExchangeId(exchangeId); final Future leaseFuture = manager.lease( exchangeId, route, null, connectionRequestTimeout, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint connectionEndpoint) { if (connectionEndpoint.isConnected()) { resultFuture.completed(connectionEndpoint); } else { final Future connectFuture = manager.connect( connectionEndpoint, getConnectionInitiator(), connectTimeout, tlsConfig, clientContext, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint result) { resultFuture.completed(result); } @Override public void failed(final Exception ex) { try { Closer.closeQuietly(connectionEndpoint); manager.release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); } finally { resultFuture.failed(ex); } } @Override public void cancelled() { try { Closer.closeQuietly(connectionEndpoint); manager.release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); } finally { resultFuture.cancel(true); } } }); resultFuture.setDependency(connectFuture); } } @Override public void failed(final Exception ex) { callback.failed(ex); } @Override public void cancelled() { callback.cancelled(); } }); resultFuture.setDependency(leaseFuture); return resultFuture; } public Future lease( final HttpHost host, final FutureCallback callback) { return lease(host, HttpClientContext.create(), callback); } public Future lease( final HttpHost host, final HttpContext context, final FutureCallback callback) { Args.notNull(host, "Host"); Args.notNull(context, "HTTP context"); final BasicFuture future = new BasicFuture<>(callback); if (!isRunning()) { future.failed(new CancellationException("Connection lease cancelled")); return future; } final HttpClientContext clientContext = HttpClientContext.adapt(context); final RequestConfig requestConfig = clientContext.getRequestConfig(); final Timeout connectionRequestTimeout = requestConfig.getConnectionRequestTimeout(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); leaseEndpoint( host, connectionRequestTimeout, connectTimeout, clientContext, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint result) { future.completed(new InternalAsyncClientEndpoint(result)); } @Override public void failed(final Exception ex) { future.failed(ex); } @Override public void cancelled() { future.cancel(true); } }); return future; } @Override public Cancellable execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { final ComplexCancellable cancellable = new ComplexCancellable(); try { if (!isRunning()) { throw new CancellationException("Request execution cancelled"); } final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : HttpClientContext.create(); exchangeHandler.produceRequest((request, entityDetails, context1) -> { RequestConfig requestConfig = null; if (request instanceof Configurable) { requestConfig = ((Configurable) request).getConfig(); } if (requestConfig != null) { clientContext.setRequestConfig(requestConfig); } else { requestConfig = clientContext.getRequestConfig(); } final Timeout connectionRequestTimeout = requestConfig.getConnectionRequestTimeout(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); final Timeout responseTimeout = requestConfig.getResponseTimeout(); final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority()); final Future leaseFuture = leaseEndpoint( target, connectionRequestTimeout, connectTimeout, clientContext, new FutureCallback() { @Override public void completed(final AsyncConnectionEndpoint connectionEndpoint) { final InternalAsyncClientEndpoint endpoint = new InternalAsyncClientEndpoint(connectionEndpoint); final AtomicInteger messageCountDown = new AtomicInteger(2); final AsyncClientExchangeHandler internalExchangeHandler = new AsyncClientExchangeHandler() { @Override public void releaseResources() { try { exchangeHandler.releaseResources(); } finally { endpoint.releaseAndDiscard(); } } @Override public void failed(final Exception cause) { try { exchangeHandler.failed(cause); } finally { endpoint.releaseAndDiscard(); } } @Override public void cancel() { failed(new RequestFailedException("Request aborted")); } @Override public void produceRequest( final RequestChannel channel, final HttpContext context1) throws HttpException, IOException { channel.sendRequest(request, entityDetails, context1); if (entityDetails == null) { messageCountDown.decrementAndGet(); } } @Override public int available() { return exchangeHandler.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { exchangeHandler.produce(new DataStreamChannel() { @Override public void requestOutput() { channel.requestOutput(); } @Override public int write(final ByteBuffer src) throws IOException { return channel.write(src); } @Override public void endStream(final List trailers) throws IOException { channel.endStream(trailers); if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } } @Override public void endStream() throws IOException { channel.endStream(); if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } } }); } @Override public void consumeInformation( final HttpResponse response, final HttpContext context1) throws HttpException, IOException { exchangeHandler.consumeInformation(response, context1); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context1) throws HttpException, IOException { exchangeHandler.consumeResponse(response, entityDetails, context1); if (response.getCode() >= HttpStatus.SC_CLIENT_ERROR) { messageCountDown.decrementAndGet(); } if (entityDetails == null) { if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } } } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { exchangeHandler.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { exchangeHandler.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { if (messageCountDown.decrementAndGet() <= 0) { endpoint.releaseAndReuse(); } exchangeHandler.streamEnd(trailers); } }; if (responseTimeout != null) { endpoint.setSocketTimeout(responseTimeout); } endpoint.execute(internalExchangeHandler, pushHandlerFactory, clientContext); } @Override public void failed(final Exception ex) { exchangeHandler.failed(ex); } @Override public void cancelled() { exchangeHandler.cancel(); } }); cancellable.setDependency(() -> leaseFuture.cancel(true)); }, context); } catch (final HttpException | IOException | IllegalStateException ex) { exchangeHandler.failed(ex); } return cancellable; } private class InternalAsyncClientEndpoint extends AsyncClientEndpoint { private final AsyncConnectionEndpoint connectionEndpoint; private final AtomicBoolean released; InternalAsyncClientEndpoint(final AsyncConnectionEndpoint connectionEndpoint) { this.connectionEndpoint = connectionEndpoint; this.released = new AtomicBoolean(false); } boolean isReleased() { return released.get(); } @Override public boolean isConnected() { return !isReleased() && connectionEndpoint.isConnected(); } @Override public void execute( final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { Asserts.check(!released.get(), "Endpoint has already been released"); final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : HttpClientContext.create(); final String exchangeId = ExecSupport.getNextExchangeId(); clientContext.setExchangeId(exchangeId); if (LOG.isDebugEnabled()) { LOG.debug("{} executing message exchange {}", exchangeId, ConnPoolSupport.getId(connectionEndpoint)); connectionEndpoint.execute( exchangeId, new LoggingAsyncClientExchangeHandler(LOG, exchangeId, exchangeHandler), pushHandlerFactory, clientContext); } else { connectionEndpoint.execute(exchangeId, exchangeHandler, clientContext); } } public void setSocketTimeout(final Timeout timeout) { connectionEndpoint.setSocketTimeout(timeout); } @Override public void releaseAndReuse() { if (released.compareAndSet(false, true)) { manager.release(connectionEndpoint, null, TimeValue.NEG_ONE_MILLISECOND); } } @Override public void releaseAndDiscard() { if (released.compareAndSet(false, true)) { Closer.closeQuietly(connectionEndpoint); manager.release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); } } } } package-info.java000066400000000000000000000024741434266521000360510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Asynchronous HTTP client API implementation that supports both * HTTP/2 and HTTP/1.1 transport. */ package org.apache.hc.client5.http.impl.async; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/000077500000000000000000000000001434266521000325565ustar00rootroot00000000000000AuthCacheKeeper.java000066400000000000000000000147641434266521000363370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthStateCacheable; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class that implements commons aspects of the client side authentication cache keeping. * * @since 5.2 */ @Internal @Contract(threading = ThreadingBehavior.STATELESS) public final class AuthCacheKeeper { private static final Logger LOG = LoggerFactory.getLogger(AuthCacheKeeper.class); private final SchemePortResolver schemePortResolver; public AuthCacheKeeper(final SchemePortResolver schemePortResolver) { this.schemePortResolver = schemePortResolver; } public void updateOnChallenge(final HttpHost host, final String pathPrefix, final AuthExchange authExchange, final HttpContext context) { clearCache(host, pathPrefix, HttpClientContext.adapt(context)); } public void updateOnNoChallenge(final HttpHost host, final String pathPrefix, final AuthExchange authExchange, final HttpContext context) { if (authExchange.getState() == AuthExchange.State.SUCCESS) { updateCache(host, pathPrefix, authExchange.getAuthScheme(), HttpClientContext.adapt(context)); } } public void updateOnResponse(final HttpHost host, final String pathPrefix, final AuthExchange authExchange, final HttpContext context) { if (authExchange.getState() == AuthExchange.State.FAILURE) { clearCache(host, pathPrefix, HttpClientContext.adapt(context)); } } public void loadPreemptively(final HttpHost host, final String pathPrefix, final AuthExchange authExchange, final HttpContext context) { if (authExchange.getState() == AuthExchange.State.UNCHALLENGED) { AuthScheme authScheme = loadFromCache(host, pathPrefix, HttpClientContext.adapt(context)); if (authScheme == null && pathPrefix != null) { authScheme = loadFromCache(host, null, HttpClientContext.adapt(context)); } if (authScheme != null) { authExchange.select(authScheme); } } } private AuthScheme loadFromCache(final HttpHost host, final String pathPrefix, final HttpClientContext clientContext) { final AuthCache authCache = clientContext.getAuthCache(); if (authCache != null) { final AuthScheme authScheme = authCache.get(host, pathPrefix); if (authScheme != null) { if (LOG.isDebugEnabled()) { final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} Re-using cached '{}' auth scheme for {}{}", exchangeId, authScheme.getName(), host, pathPrefix != null ? pathPrefix : ""); } return authScheme; } } return null; } private void updateCache(final HttpHost host, final String pathPrefix, final AuthScheme authScheme, final HttpClientContext clientContext) { final boolean cacheable = authScheme.getClass().getAnnotation(AuthStateCacheable.class) != null; if (cacheable) { AuthCache authCache = clientContext.getAuthCache(); if (authCache == null) { authCache = new BasicAuthCache(schemePortResolver); clientContext.setAuthCache(authCache); } if (LOG.isDebugEnabled()) { final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} Caching '{}' auth scheme for {}{}", exchangeId, authScheme.getName(), host, pathPrefix != null ? pathPrefix : ""); } authCache.put(host, pathPrefix, authScheme); } } private void clearCache(final HttpHost host, final String pathPrefix, final HttpClientContext clientContext) { final AuthCache authCache = clientContext.getAuthCache(); if (authCache != null) { if (LOG.isDebugEnabled()) { final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} Clearing cached auth scheme for {}{}", exchangeId, host, pathPrefix != null ? pathPrefix : ""); } authCache.remove(host, pathPrefix); } } } AuthChallengeParser.java000066400000000000000000000177071434266521000372370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * Authentication challenge parser. * * @since 5.0 */ public class AuthChallengeParser { public static final AuthChallengeParser INSTANCE = new AuthChallengeParser(); private final Tokenizer tokenParser = Tokenizer.INSTANCE; private final static char BLANK = ' '; private final static char COMMA_CHAR = ','; private final static char EQUAL_CHAR = '='; // IMPORTANT! // These private static variables must be treated as immutable and never exposed outside this class private static final BitSet TERMINATORS = Tokenizer.INIT_BITSET(BLANK, EQUAL_CHAR, COMMA_CHAR); private static final BitSet DELIMITER = Tokenizer.INIT_BITSET(COMMA_CHAR); private static final BitSet SPACE = Tokenizer.INIT_BITSET(BLANK); static class ChallengeInt { final String schemeName; final List params; ChallengeInt(final String schemeName) { this.schemeName = schemeName; this.params = new ArrayList<>(); } @Override public String toString() { return "ChallengeInternal{" + "schemeName='" + schemeName + '\'' + ", params=" + params + '}'; } } /** * Parses the given sequence of characters into a list of {@link AuthChallenge} elements. * * @param challengeType the type of challenge (target or proxy). * @param buffer the sequence of characters to be parsed. * @param cursor the parser cursor. * @return a list of auth challenge elements. */ public List parse( final ChallengeType challengeType, final CharSequence buffer, final ParserCursor cursor) throws ParseException { tokenParser.skipWhiteSpace(buffer, cursor); if (cursor.atEnd()) { throw new ParseException("Malformed auth challenge"); } final List internalChallenges = new ArrayList<>(); final String schemeName = tokenParser.parseToken(buffer, cursor, SPACE); if (TextUtils.isBlank(schemeName)) { throw new ParseException("Malformed auth challenge"); } ChallengeInt current = new ChallengeInt(schemeName); while (current != null) { internalChallenges.add(current); current = parseChallenge(buffer, cursor, current); } final List challenges = new ArrayList<>(internalChallenges.size()); for (final ChallengeInt internal : internalChallenges) { final List params = internal.params; String token68 = null; if (params.size() == 1) { final NameValuePair param = params.get(0); if (param.getValue() == null) { token68 = param.getName(); params.clear(); } } challenges.add( new AuthChallenge(challengeType, internal.schemeName, token68, !params.isEmpty() ? params : null)); } return challenges; } ChallengeInt parseChallenge( final CharSequence buffer, final ParserCursor cursor, final ChallengeInt currentChallenge) throws ParseException { for (;;) { tokenParser.skipWhiteSpace(buffer, cursor); if (cursor.atEnd()) { return null; } final String token = parseToken(buffer, cursor); if (TextUtils.isBlank(token)) { throw new ParseException("Malformed auth challenge"); } tokenParser.skipWhiteSpace(buffer, cursor); // it gets really messy here if (cursor.atEnd()) { // at the end of the header currentChallenge.params.add(new BasicNameValuePair(token, null)); } else { char ch = buffer.charAt(cursor.getPos()); if (ch == EQUAL_CHAR) { cursor.updatePos(cursor.getPos() + 1); final String value = tokenParser.parseValue(buffer, cursor, DELIMITER); tokenParser.skipWhiteSpace(buffer, cursor); if (!cursor.atEnd()) { ch = buffer.charAt(cursor.getPos()); if (ch == COMMA_CHAR) { cursor.updatePos(cursor.getPos() + 1); } } currentChallenge.params.add(new BasicNameValuePair(token, value)); } else if (ch == COMMA_CHAR) { cursor.updatePos(cursor.getPos() + 1); currentChallenge.params.add(new BasicNameValuePair(token, null)); } else { // the token represents new challenge if (currentChallenge.params.isEmpty()) { throw new ParseException("Malformed auth challenge"); } return new ChallengeInt(token); } } } } String parseToken(final CharSequence buf, final ParserCursor cursor) { final StringBuilder dst = new StringBuilder(); while (!cursor.atEnd()) { int pos = cursor.getPos(); char current = buf.charAt(pos); if (TERMINATORS.get(current)) { // Here it gets really ugly if (current == EQUAL_CHAR) { // it can be a start of a parameter value or token68 padding // Look ahead and see if there are more '=' or at end of buffer if (pos + 1 < cursor.getUpperBound() && buf.charAt(pos + 1) != EQUAL_CHAR) { break; } do { dst.append(current); pos++; cursor.updatePos(pos); if (cursor.atEnd()) { break; } current = buf.charAt(pos); } while (current == EQUAL_CHAR); } else { break; } } else { dst.append(current); cursor.updatePos(pos + 1); } } return dst.toString(); } } AuthSchemeSupport.java000066400000000000000000000035561434266521000367760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.core5.annotation.Internal; /** * @since 5.2 */ @Internal public class AuthSchemeSupport { public static Charset parseCharset( final String charsetName, final Charset defaultCharset) throws AuthenticationException { try { return charsetName != null ? Charset.forName(charsetName) : defaultCharset; } catch (final UnsupportedCharsetException ex) { throw new AuthenticationException("Unsupported charset: " + charsetName); } } } BasicAuthCache.java000066400000000000000000000172431434266521000361400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.LangUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default implementation of {@link AuthCache}. This implements * expects {@link org.apache.hc.client5.http.auth.AuthScheme} to be {@link java.io.Serializable} * in order to be cacheable. *

* Instances of this class are thread safe as of version 4.4. *

* * @since 4.1 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class BasicAuthCache implements AuthCache { private static final Logger LOG = LoggerFactory.getLogger(BasicAuthCache.class); static class Key { final String scheme; final String host; final int port; final String pathPrefix; Key(final String scheme, final String host, final int port, final String pathPrefix) { Args.notBlank(scheme, "Scheme"); Args.notBlank(host, "Scheme"); this.scheme = scheme.toLowerCase(Locale.ROOT); this.host = host.toLowerCase(Locale.ROOT); this.port = port; this.pathPrefix = pathPrefix; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof Key) { final Key that = (Key) obj; return this.scheme.equals(that.scheme) && this.host.equals(that.host) && this.port == that.port && Objects.equals(this.pathPrefix, that.pathPrefix); } return false; } @Override public int hashCode() { int hash = LangUtils.HASH_SEED; hash = LangUtils.hashCode(hash, this.scheme); hash = LangUtils.hashCode(hash, this.host); hash = LangUtils.hashCode(hash, this.port); hash = LangUtils.hashCode(hash, this.pathPrefix); return hash; } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append(scheme).append("://").append(host); if (port >= 0) { buf.append(":").append(port); } if (pathPrefix != null) { if (!pathPrefix.startsWith("/")) { buf.append("/"); } buf.append(pathPrefix); } return buf.toString(); } } private final Map map; private final SchemePortResolver schemePortResolver; /** * Default constructor. * * @since 4.3 */ public BasicAuthCache(final SchemePortResolver schemePortResolver) { super(); this.map = new ConcurrentHashMap<>(); this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; } public BasicAuthCache() { this(null); } private Key key(final String scheme, final NamedEndpoint authority, final String pathPrefix) { return new Key(scheme, authority.getHostName(), schemePortResolver.resolve(scheme, authority), pathPrefix); } @Override public void put(final HttpHost host, final AuthScheme authScheme) { put(host, null, authScheme); } @Override public AuthScheme get(final HttpHost host) { return get(host, null); } @Override public void remove(final HttpHost host) { remove(host, null); } @Override public void put(final HttpHost host, final String pathPrefix, final AuthScheme authScheme) { Args.notNull(host, "HTTP host"); if (authScheme == null) { return; } if (authScheme instanceof Serializable) { try { final ByteArrayOutputStream buf = new ByteArrayOutputStream(); try (final ObjectOutputStream out = new ObjectOutputStream(buf)) { out.writeObject(authScheme); } this.map.put(key(host.getSchemeName(), host, pathPrefix), buf.toByteArray()); } catch (final IOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("Unexpected I/O error while serializing auth scheme", ex); } } } else { if (LOG.isDebugEnabled()) { LOG.debug("Auth scheme {} is not serializable", authScheme.getClass()); } } } @Override public AuthScheme get(final HttpHost host, final String pathPrefix) { Args.notNull(host, "HTTP host"); final byte[] bytes = this.map.get(key(host.getSchemeName(), host, pathPrefix)); if (bytes != null) { try { final ByteArrayInputStream buf = new ByteArrayInputStream(bytes); try (final ObjectInputStream in = new ObjectInputStream(buf)) { return (AuthScheme) in.readObject(); } } catch (final IOException ex) { if (LOG.isWarnEnabled()) { LOG.warn("Unexpected I/O error while de-serializing auth scheme", ex); } } catch (final ClassNotFoundException ex) { if (LOG.isWarnEnabled()) { LOG.warn("Unexpected error while de-serializing auth scheme", ex); } } } return null; } @Override public void remove(final HttpHost host, final String pathPrefix) { Args.notNull(host, "HTTP host"); this.map.remove(key(host.getSchemeName(), host, pathPrefix)); } @Override public void clear() { this.map.clear(); } @Override public String toString() { return this.map.toString(); } } BasicCredentialsProvider.java000066400000000000000000000051511434266521000402560ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsStore; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Default implementation of {@link CredentialsStore}. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public class BasicCredentialsProvider implements CredentialsStore { private final ConcurrentHashMap credMap; /** * Default constructor. */ public BasicCredentialsProvider() { super(); this.credMap = new ConcurrentHashMap<>(); } @Override public void setCredentials( final AuthScope authScope, final Credentials credentials) { Args.notNull(authScope, "Authentication scope"); credMap.put(authScope, credentials); } @Override public Credentials getCredentials(final AuthScope authScope, final HttpContext context) { return CredentialsMatcher.matchCredentials(this.credMap, authScope); } @Override public void clear() { this.credMap.clear(); } @Override public String toString() { return credMap.keySet().toString(); } } BasicScheme.java000066400000000000000000000177721434266521000355260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.security.Principal; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.hc.client5.http.utils.Base64; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthStateCacheable; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.ByteArrayBuilder; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Basic authentication scheme as defined in RFC 2617. * * @since 4.0 */ @AuthStateCacheable public class BasicScheme implements AuthScheme, Serializable { private static final long serialVersionUID = -1931571557597830536L; private static final Logger LOG = LoggerFactory.getLogger(BasicScheme.class); private final Map paramMap; private transient Charset defaultCharset; private transient ByteArrayBuilder buffer; private transient Base64 base64codec; private boolean complete; private String username; private char[] password; /** * @since 4.3 */ public BasicScheme(final Charset charset) { this.paramMap = new HashMap<>(); this.defaultCharset = charset != null ? charset : StandardCharsets.US_ASCII; this.complete = false; } public BasicScheme() { this(StandardCharsets.US_ASCII); } private void applyCredentials(final Credentials credentials) { this.username = credentials.getUserPrincipal().getName(); this.password = credentials.getPassword(); } private void clearCredentials() { this.username = null; this.password = null; } public void initPreemptive(final Credentials credentials) { if (credentials != null) { applyCredentials(credentials); } else { clearCredentials(); } } @Override public String getName() { return StandardAuthScheme.BASIC; } @Override public boolean isConnectionBased() { return false; } @Override public String getRealm() { return this.paramMap.get("realm"); } @Override public void processChallenge( final AuthChallenge authChallenge, final HttpContext context) throws MalformedChallengeException { this.paramMap.clear(); final List params = authChallenge.getParams(); if (params != null) { for (final NameValuePair param: params) { this.paramMap.put(param.getName().toLowerCase(Locale.ROOT), param.getValue()); } } this.complete = true; } @Override public boolean isChallengeComplete() { return this.complete; } @Override public boolean isResponseReady( final HttpHost host, final CredentialsProvider credentialsProvider, final HttpContext context) throws AuthenticationException { Args.notNull(host, "Auth host"); Args.notNull(credentialsProvider, "CredentialsProvider"); final AuthScope authScope = new AuthScope(host, getRealm(), getName()); final Credentials credentials = credentialsProvider.getCredentials( authScope, context); if (credentials != null) { applyCredentials(credentials); return true; } if (LOG.isDebugEnabled()) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} No credentials found for auth scope [{}]", exchangeId, authScope); } clearCredentials(); return false; } @Override public Principal getPrincipal() { return null; } private void validateUsername() throws AuthenticationException { if (username == null) { throw new AuthenticationException("User credentials not set"); } for (int i = 0; i < username.length(); i++) { final char ch = username.charAt(i); if (Character.isISOControl(ch)) { throw new AuthenticationException("Username must not contain any control characters"); } if (ch == ':') { throw new AuthenticationException("Username contains a colon character and is invalid"); } } } @Override public String generateAuthResponse( final HttpHost host, final HttpRequest request, final HttpContext context) throws AuthenticationException { validateUsername(); if (this.buffer == null) { this.buffer = new ByteArrayBuilder(64); } else { this.buffer.reset(); } final Charset charset = AuthSchemeSupport.parseCharset(paramMap.get("charset"), defaultCharset); this.buffer.charset(charset); this.buffer.append(this.username).append(":").append(this.password); if (this.base64codec == null) { this.base64codec = new Base64(); } final byte[] encodedCreds = this.base64codec.encode(this.buffer.toByteArray()); this.buffer.reset(); return StandardAuthScheme.BASIC + " " + new String(encodedCreds, 0, encodedCreds.length, StandardCharsets.US_ASCII); } private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeUTF(this.defaultCharset.name()); } @SuppressWarnings("unchecked") private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); try { this.defaultCharset = Charset.forName(in.readUTF()); } catch (final UnsupportedCharsetException ex) { this.defaultCharset = StandardCharsets.US_ASCII; } } private void readObjectNoData() { } @Override public String toString() { return getName() + this.paramMap; } } BasicSchemeFactory.java000066400000000000000000000043671434266521000370520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.nio.charset.Charset; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link AuthSchemeFactory} implementation that creates and initializes * {@link BasicScheme} instances. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicSchemeFactory implements AuthSchemeFactory { /** * Singleton instance with a null Charset. */ public static final BasicSchemeFactory INSTANCE = new BasicSchemeFactory(); private final Charset charset; /** * @since 4.3 */ public BasicSchemeFactory(final Charset charset) { super(); this.charset = charset; } public BasicSchemeFactory() { this(null); } @Override public AuthScheme create(final HttpContext context) { return new BasicScheme(this.charset); } } CredentialsMatcher.java000066400000000000000000000045731434266521000371140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.util.Map; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; final class CredentialsMatcher { /** * Find matching {@link Credentials credentials} for the given authentication scope. * * @param map the credentials hash map * @param authScope the {@link AuthScope authentication scope} * @return the credentials * */ static Credentials matchCredentials(final Map map, final AuthScope authScope) { // see if we get a direct hit Credentials creds = map.get(authScope); if (creds == null) { // Nope. // Do a full scan int bestMatchFactor = -1; AuthScope bestMatch = null; for (final AuthScope current: map.keySet()) { final int factor = authScope.match(current); if (factor > bestMatchFactor) { bestMatchFactor = factor; bestMatch = current; } } if (bestMatch != null) { creds = map.get(bestMatch); } } return creds; } } CredentialsProviderBuilder.java000066400000000000000000000071611434266521000406260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.Args; /** * {@link CredentialsProvider} builder. * * @since 5.2 */ public final class CredentialsProviderBuilder { private final Map credMap; public static CredentialsProviderBuilder create() { return new CredentialsProviderBuilder(); } public CredentialsProviderBuilder() { super(); this.credMap = new HashMap<>(); } public CredentialsProviderBuilder add(final AuthScope authScope, final Credentials credentials) { Args.notNull(authScope, "Host"); credMap.put(authScope, credentials); return this; } public CredentialsProviderBuilder add(final AuthScope authScope, final String username, final char[] password) { Args.notNull(authScope, "Host"); credMap.put(authScope, new UsernamePasswordCredentials(username, password)); return this; } public CredentialsProviderBuilder add(final HttpHost httpHost, final Credentials credentials) { Args.notNull(httpHost, "Host"); credMap.put(new AuthScope(httpHost), credentials); return this; } public CredentialsProviderBuilder add(final HttpHost httpHost, final String username, final char[] password) { Args.notNull(httpHost, "Host"); credMap.put(new AuthScope(httpHost), new UsernamePasswordCredentials(username, password)); return this; } public CredentialsProvider build() { if (credMap.size() == 0) { return new BasicCredentialsProvider(); } else if (credMap.size() == 1) { final Map.Entry entry = credMap.entrySet().iterator().next(); return new SingleCredentialsProvider(entry.getKey(), entry.getValue()); } else { return new FixedCredentialsProvider(credMap); } } static class Entry { final AuthScope authScope; final Credentials credentials; Entry(final AuthScope authScope, final Credentials credentials) { this.authScope = authScope; this.credentials = credentials; } } } DigestScheme.java000066400000000000000000000433161434266521000357150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.Principal; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Formatter; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.utils.ByteArrayBuilder; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicHeaderValueFormatter; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Digest authentication scheme as defined in RFC 2617. * Both MD5 (default) and MD5-sess are supported. * Currently only qop=auth or no qop is supported. qop=auth-int * is unsupported. If auth and auth-int are provided, auth is * used. *

* Since the digest username is included as clear text in the generated * Authentication header, the charset of the username must be compatible * with the HTTP element charset used by the connection. *

* * @since 4.0 */ public class DigestScheme implements AuthScheme, Serializable { private static final long serialVersionUID = 3883908186234566916L; private static final Logger LOG = LoggerFactory.getLogger(DigestScheme.class); /** * Hexa values used when creating 32 character long digest in HTTP DigestScheme * in case of authentication. * * @see #formatHex(byte[]) */ private static final char[] HEXADECIMAL = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Represent the possible values of quality of protection. */ private enum QualityOfProtection { UNKNOWN, MISSING, AUTH_INT, AUTH } private transient Charset defaultCharset; private final Map paramMap; private boolean complete; private transient ByteArrayBuilder buffer; private String lastNonce; private long nounceCount; private String cnonce; private byte[] a1; private byte[] a2; private String username; private char[] password; public DigestScheme() { this(StandardCharsets.ISO_8859_1); } public DigestScheme(final Charset charset) { this.defaultCharset = charset != null ? charset : StandardCharsets.ISO_8859_1; this.paramMap = new HashMap<>(); this.complete = false; } public void initPreemptive(final Credentials credentials, final String cnonce, final String realm) { Args.notNull(credentials, "Credentials"); this.username = credentials.getUserPrincipal().getName(); this.password = credentials.getPassword(); this.paramMap.put("cnonce", cnonce); this.paramMap.put("realm", realm); } @Override public String getName() { return StandardAuthScheme.DIGEST; } @Override public boolean isConnectionBased() { return false; } @Override public String getRealm() { return this.paramMap.get("realm"); } @Override public void processChallenge( final AuthChallenge authChallenge, final HttpContext context) throws MalformedChallengeException { Args.notNull(authChallenge, "AuthChallenge"); this.paramMap.clear(); final List params = authChallenge.getParams(); if (params != null) { for (final NameValuePair param: params) { this.paramMap.put(param.getName().toLowerCase(Locale.ROOT), param.getValue()); } } if (this.paramMap.isEmpty()) { throw new MalformedChallengeException("Missing digest auth parameters"); } this.complete = true; } @Override public boolean isChallengeComplete() { final String s = this.paramMap.get("stale"); return !"true".equalsIgnoreCase(s) && this.complete; } @Override public boolean isResponseReady( final HttpHost host, final CredentialsProvider credentialsProvider, final HttpContext context) throws AuthenticationException { Args.notNull(host, "Auth host"); Args.notNull(credentialsProvider, "CredentialsProvider"); final AuthScope authScope = new AuthScope(host, getRealm(), getName()); final Credentials credentials = credentialsProvider.getCredentials( authScope, context); if (credentials != null) { this.username = credentials.getUserPrincipal().getName(); this.password = credentials.getPassword(); return true; } if (LOG.isDebugEnabled()) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} No credentials found for auth scope [{}]", exchangeId, authScope); } this.username = null; this.password = null; return false; } @Override public Principal getPrincipal() { return null; } @Override public String generateAuthResponse( final HttpHost host, final HttpRequest request, final HttpContext context) throws AuthenticationException { Args.notNull(request, "HTTP request"); if (this.paramMap.get("realm") == null) { throw new AuthenticationException("missing realm"); } if (this.paramMap.get("nonce") == null) { throw new AuthenticationException("missing nonce"); } return createDigestResponse(request); } private static MessageDigest createMessageDigest( final String digAlg) throws UnsupportedDigestAlgorithmException { try { return MessageDigest.getInstance(digAlg); } catch (final Exception e) { throw new UnsupportedDigestAlgorithmException( "Unsupported algorithm in HTTP Digest authentication: " + digAlg); } } private String createDigestResponse(final HttpRequest request) throws AuthenticationException { final String uri = request.getRequestUri(); final String method = request.getMethod(); final String realm = this.paramMap.get("realm"); final String nonce = this.paramMap.get("nonce"); final String opaque = this.paramMap.get("opaque"); String algorithm = this.paramMap.get("algorithm"); // If an algorithm is not specified, default to MD5. if (algorithm == null) { algorithm = "MD5"; } final Set qopset = new HashSet<>(8); QualityOfProtection qop = QualityOfProtection.UNKNOWN; final String qoplist = this.paramMap.get("qop"); if (qoplist != null) { final StringTokenizer tok = new StringTokenizer(qoplist, ","); while (tok.hasMoreTokens()) { final String variant = tok.nextToken().trim(); qopset.add(variant.toLowerCase(Locale.ROOT)); } final HttpEntity entity = request instanceof ClassicHttpRequest ? ((ClassicHttpRequest) request).getEntity() : null; if (entity != null && qopset.contains("auth-int")) { qop = QualityOfProtection.AUTH_INT; } else if (qopset.contains("auth")) { qop = QualityOfProtection.AUTH; } else if (qopset.contains("auth-int")) { qop = QualityOfProtection.AUTH_INT; } } else { qop = QualityOfProtection.MISSING; } if (qop == QualityOfProtection.UNKNOWN) { throw new AuthenticationException("None of the qop methods is supported: " + qoplist); } final Charset charset = AuthSchemeSupport.parseCharset(paramMap.get("charset"), defaultCharset); String digAlg = algorithm; if (digAlg.equalsIgnoreCase("MD5-sess")) { digAlg = "MD5"; } final MessageDigest digester; try { digester = createMessageDigest(digAlg); } catch (final UnsupportedDigestAlgorithmException ex) { throw new AuthenticationException("Unsupported digest algorithm: " + digAlg); } if (nonce.equals(this.lastNonce)) { nounceCount++; } else { nounceCount = 1; cnonce = null; lastNonce = nonce; } final StringBuilder sb = new StringBuilder(8); try (final Formatter formatter = new Formatter(sb, Locale.ROOT)) { formatter.format("%08x", nounceCount); } final String nc = sb.toString(); if (cnonce == null) { cnonce = formatHex(createCnonce()); } if (buffer == null) { buffer = new ByteArrayBuilder(128); } else { buffer.reset(); } buffer.charset(charset); a1 = null; a2 = null; // 3.2.2.2: Calculating digest if (algorithm.equalsIgnoreCase("MD5-sess")) { // H( unq(username-value) ":" unq(realm-value) ":" passwd ) // ":" unq(nonce-value) // ":" unq(cnonce-value) // calculated one per session buffer.append(username).append(":").append(realm).append(":").append(password); final String checksum = formatHex(digester.digest(this.buffer.toByteArray())); buffer.reset(); buffer.append(checksum).append(":").append(nonce).append(":").append(cnonce); } else { // unq(username-value) ":" unq(realm-value) ":" passwd buffer.append(username).append(":").append(realm).append(":").append(password); } a1 = buffer.toByteArray(); final String hasha1 = formatHex(digester.digest(a1)); buffer.reset(); if (qop == QualityOfProtection.AUTH) { // Method ":" digest-uri-value a2 = buffer.append(method).append(":").append(uri).toByteArray(); } else if (qop == QualityOfProtection.AUTH_INT) { // Method ":" digest-uri-value ":" H(entity-body) final HttpEntity entity = request instanceof ClassicHttpRequest ? ((ClassicHttpRequest) request).getEntity() : null; if (entity != null && !entity.isRepeatable()) { // If the entity is not repeatable, try falling back onto QOP_AUTH if (qopset.contains("auth")) { qop = QualityOfProtection.AUTH; a2 = buffer.append(method).append(":").append(uri).toByteArray(); } else { throw new AuthenticationException("Qop auth-int cannot be used with " + "a non-repeatable entity"); } } else { final HttpEntityDigester entityDigester = new HttpEntityDigester(digester); try { if (entity != null) { entity.writeTo(entityDigester); } entityDigester.close(); } catch (final IOException ex) { throw new AuthenticationException("I/O error reading entity content", ex); } a2 = buffer.append(method).append(":").append(uri) .append(":").append(formatHex(entityDigester.getDigest())).toByteArray(); } } else { a2 = buffer.append(method).append(":").append(uri).toByteArray(); } final String hasha2 = formatHex(digester.digest(a2)); buffer.reset(); // 3.2.2.1 final byte[] digestInput; if (qop == QualityOfProtection.MISSING) { buffer.append(hasha1).append(":").append(nonce).append(":").append(hasha2); } else { buffer.append(hasha1).append(":").append(nonce).append(":").append(nc).append(":") .append(cnonce).append(":").append(qop == QualityOfProtection.AUTH_INT ? "auth-int" : "auth") .append(":").append(hasha2); } digestInput = buffer.toByteArray(); buffer.reset(); final String digest = formatHex(digester.digest(digestInput)); final CharArrayBuffer buffer = new CharArrayBuffer(128); buffer.append(StandardAuthScheme.DIGEST + " "); final List params = new ArrayList<>(20); params.add(new BasicNameValuePair("username", username)); params.add(new BasicNameValuePair("realm", realm)); params.add(new BasicNameValuePair("nonce", nonce)); params.add(new BasicNameValuePair("uri", uri)); params.add(new BasicNameValuePair("response", digest)); if (qop != QualityOfProtection.MISSING) { params.add(new BasicNameValuePair("qop", qop == QualityOfProtection.AUTH_INT ? "auth-int" : "auth")); params.add(new BasicNameValuePair("nc", nc)); params.add(new BasicNameValuePair("cnonce", cnonce)); } // algorithm cannot be null here params.add(new BasicNameValuePair("algorithm", algorithm)); if (opaque != null) { params.add(new BasicNameValuePair("opaque", opaque)); } for (int i = 0; i < params.size(); i++) { final BasicNameValuePair param = params.get(i); if (i > 0) { buffer.append(", "); } final String name = param.getName(); final boolean noQuotes = ("nc".equals(name) || "qop".equals(name) || "algorithm".equals(name)); BasicHeaderValueFormatter.INSTANCE.formatNameValuePair(buffer, param, !noQuotes); } return buffer.toString(); } @Internal public String getNonce() { return lastNonce; } @Internal public long getNounceCount() { return nounceCount; } @Internal public String getCnonce() { return cnonce; } String getA1() { return a1 != null ? new String(a1, StandardCharsets.US_ASCII) : null; } String getA2() { return a2 != null ? new String(a2, StandardCharsets.US_ASCII) : null; } /** * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long * {@code String} according to RFC 2617. * * @param binaryData array containing the digest * @return encoded MD5, or {@code null} if encoding failed */ static String formatHex(final byte[] binaryData) { final int n = binaryData.length; final char[] buffer = new char[n * 2]; for (int i = 0; i < n; i++) { final int low = (binaryData[i] & 0x0f); final int high = ((binaryData[i] & 0xf0) >> 4); buffer[i * 2] = HEXADECIMAL[high]; buffer[(i * 2) + 1] = HEXADECIMAL[low]; } return new String(buffer); } /** * Creates a random cnonce value based on the current time. * * @return The cnonce value as String. */ static byte[] createCnonce() { final SecureRandom rnd = new SecureRandom(); final byte[] tmp = new byte[8]; rnd.nextBytes(tmp); return tmp; } private void writeObject(final ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeUTF(defaultCharset.name()); } private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); this.defaultCharset = Charset.forName(in.readUTF()); } @Override public String toString() { return getName() + this.paramMap; } } DigestSchemeFactory.java000066400000000000000000000043241434266521000372410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.nio.charset.Charset; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link AuthSchemeFactory} implementation that creates and initializes * {@link DigestScheme} instances. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DigestSchemeFactory implements AuthSchemeFactory { /** * Singleton instance. */ public static final DigestSchemeFactory INSTANCE = new DigestSchemeFactory(); private final Charset charset; /** * @since 5.1 */ public DigestSchemeFactory(final Charset charset) { this.charset = charset; } public DigestSchemeFactory() { this(null); } @Override public AuthScheme create(final HttpContext context) { return new DigestScheme(charset); } } FixedCredentialsProvider.java000066400000000000000000000041151434266521000402730ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.core5.http.protocol.HttpContext; final class FixedCredentialsProvider implements CredentialsProvider { private final Map credMap; public FixedCredentialsProvider(final Map credMap) { super(); this.credMap = Collections.unmodifiableMap(new HashMap<>(credMap)); } @Override public Credentials getCredentials(final AuthScope authScope, final HttpContext context) { return CredentialsMatcher.matchCredentials(this.credMap, authScope); } @Override public String toString() { return credMap.keySet().toString(); } } GGSSchemeBase.java000066400000000000000000000245361434266521000357140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.net.UnknownHostException; import java.security.Principal; import org.apache.hc.client5.http.utils.Base64; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.InvalidCredentialsException; import org.apache.hc.client5.http.auth.KerberosConfig; import org.apache.hc.client5.http.auth.KerberosCredentials; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSName; import org.ietf.jgss.Oid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Common behavior for {@code GSS} based authentication schemes. * * @since 4.2 */ public abstract class GGSSchemeBase implements AuthScheme { enum State { UNINITIATED, CHALLENGE_RECEIVED, TOKEN_GENERATED, FAILED, } private static final Logger LOG = LoggerFactory.getLogger(GGSSchemeBase.class); private static final String NO_TOKEN = ""; private static final String KERBEROS_SCHEME = "HTTP"; private final KerberosConfig config; private final DnsResolver dnsResolver; /** Authentication process state */ private State state; private GSSCredential gssCredential; private String challenge; private byte[] token; GGSSchemeBase(final KerberosConfig config, final DnsResolver dnsResolver) { super(); this.config = config != null ? config : KerberosConfig.DEFAULT; this.dnsResolver = dnsResolver != null ? dnsResolver : SystemDefaultDnsResolver.INSTANCE; this.state = State.UNINITIATED; } GGSSchemeBase(final KerberosConfig config) { this(config, SystemDefaultDnsResolver.INSTANCE); } GGSSchemeBase() { this(KerberosConfig.DEFAULT, SystemDefaultDnsResolver.INSTANCE); } @Override public String getRealm() { return null; } @Override public void processChallenge( final AuthChallenge authChallenge, final HttpContext context) throws MalformedChallengeException { Args.notNull(authChallenge, "AuthChallenge"); this.challenge = authChallenge.getValue() != null ? authChallenge.getValue() : NO_TOKEN; if (state == State.UNINITIATED) { token = Base64.decodeBase64(challenge.getBytes()); state = State.CHALLENGE_RECEIVED; } else { if (LOG.isDebugEnabled()) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} Authentication already attempted", exchangeId); } state = State.FAILED; } } protected GSSManager getManager() { return GSSManager.getInstance(); } /** * @since 4.4 */ protected byte[] generateGSSToken( final byte[] input, final Oid oid, final String serviceName, final String authServer) throws GSSException { final GSSManager manager = getManager(); final GSSName serverName = manager.createName(serviceName + "@" + authServer, GSSName.NT_HOSTBASED_SERVICE); final GSSContext gssContext = createGSSContext(manager, oid, serverName, gssCredential); if (input != null) { return gssContext.initSecContext(input, 0, input.length); } else { return gssContext.initSecContext(new byte[] {}, 0, 0); } } /** * @since 5.0 */ protected GSSContext createGSSContext( final GSSManager manager, final Oid oid, final GSSName serverName, final GSSCredential gssCredential) throws GSSException { final GSSContext gssContext = manager.createContext(serverName.canonicalize(oid), oid, gssCredential, GSSContext.DEFAULT_LIFETIME); gssContext.requestMutualAuth(true); if (config.getRequestDelegCreds() != KerberosConfig.Option.DEFAULT) { gssContext.requestCredDeleg(config.getRequestDelegCreds() == KerberosConfig.Option.ENABLE); } return gssContext; } /** * @since 4.4 */ protected abstract byte[] generateToken(byte[] input, String serviceName, String authServer) throws GSSException; @Override public boolean isChallengeComplete() { return this.state == State.TOKEN_GENERATED || this.state == State.FAILED; } @Override public boolean isResponseReady( final HttpHost host, final CredentialsProvider credentialsProvider, final HttpContext context) throws AuthenticationException { Args.notNull(host, "Auth host"); Args.notNull(credentialsProvider, "CredentialsProvider"); final Credentials credentials = credentialsProvider.getCredentials( new AuthScope(host, null, getName()), context); if (credentials instanceof KerberosCredentials) { this.gssCredential = ((KerberosCredentials) credentials).getGSSCredential(); } else { this.gssCredential = null; } return true; } @Override public Principal getPrincipal() { return null; } @Override public String generateAuthResponse( final HttpHost host, final HttpRequest request, final HttpContext context) throws AuthenticationException { Args.notNull(host, "HTTP host"); Args.notNull(request, "HTTP request"); switch (state) { case UNINITIATED: throw new AuthenticationException(getName() + " authentication has not been initiated"); case FAILED: throw new AuthenticationException(getName() + " authentication has failed"); case CHALLENGE_RECEIVED: try { final String authServer; String hostname = host.getHostName(); if (config.getUseCanonicalHostname() != KerberosConfig.Option.DISABLE){ try { hostname = dnsResolver.resolveCanonicalHostname(host.getHostName()); } catch (final UnknownHostException ignore){ } } if (config.getStripPort() != KerberosConfig.Option.DISABLE) { authServer = hostname; } else { authServer = hostname + ":" + host.getPort(); } if (LOG.isDebugEnabled()) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} init {}", exchangeId, authServer); } token = generateToken(token, KERBEROS_SCHEME, authServer); state = State.TOKEN_GENERATED; } catch (final GSSException gsse) { state = State.FAILED; if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) { throw new InvalidCredentialsException(gsse.getMessage(), gsse); } if (gsse.getMajor() == GSSException.NO_CRED ) { throw new InvalidCredentialsException(gsse.getMessage(), gsse); } if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN || gsse.getMajor() == GSSException.DUPLICATE_TOKEN || gsse.getMajor() == GSSException.OLD_TOKEN) { throw new AuthenticationException(gsse.getMessage(), gsse); } // other error throw new AuthenticationException(gsse.getMessage()); } case TOKEN_GENERATED: final Base64 codec = new Base64(0); final String tokenstr = new String(codec.encode(token)); if (LOG.isDebugEnabled()) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} Sending response '{}' back to the auth server", exchangeId, tokenstr); } return StandardAuthScheme.SPNEGO + " " + tokenstr; default: throw new IllegalStateException("Illegal state: " + state); } } @Override public String toString() { return getName() + "{" + this.state + " " + challenge + '}'; } } HttpAuthenticator.java000066400000000000000000000363131434266521000370220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Queue; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.CharArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class that implements commons aspects of the client side HTTP authentication. *

* Please note that since version 5.2 this class no longer updated the authentication cache * bound to the execution context. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public final class HttpAuthenticator { private static final Logger LOG = LoggerFactory.getLogger(HttpAuthenticator.class); private final AuthChallengeParser parser; public HttpAuthenticator() { this.parser = new AuthChallengeParser(); } /** * Determines whether the given response represents an authentication challenge. * * @param host the hostname of the opposite endpoint. * @param challengeType the challenge type (target or proxy). * @param response the response message head. * @param authExchange the current authentication exchange state. * @param context the current execution context. * @return {@code true} if the response message represents an authentication challenge, * {@code false} otherwise. */ public boolean isChallenged( final HttpHost host, final ChallengeType challengeType, final HttpResponse response, final AuthExchange authExchange, final HttpContext context) { final int challengeCode; switch (challengeType) { case TARGET: challengeCode = HttpStatus.SC_UNAUTHORIZED; break; case PROXY: challengeCode = HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED; break; default: throw new IllegalStateException("Unexpected challenge type: " + challengeType); } final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); if (response.getCode() == challengeCode) { if (LOG.isDebugEnabled()) { LOG.debug("{} Authentication required", exchangeId); } return true; } switch (authExchange.getState()) { case CHALLENGED: case HANDSHAKE: if (LOG.isDebugEnabled()) { LOG.debug("{} Authentication succeeded", exchangeId); } authExchange.setState(AuthExchange.State.SUCCESS); break; case SUCCESS: break; default: authExchange.setState(AuthExchange.State.UNCHALLENGED); } return false; } /** * Updates the {@link AuthExchange} state based on the challenge presented in the response message * using the given {@link AuthenticationStrategy}. * * @param host the hostname of the opposite endpoint. * @param challengeType the challenge type (target or proxy). * @param response the response message head. * @param authStrategy the authentication strategy. * @param authExchange the current authentication exchange state. * @param context the current execution context. * @return {@code true} if the authentication state has been updated, * {@code false} if unchanged. */ public boolean updateAuthState( final HttpHost host, final ChallengeType challengeType, final HttpResponse response, final AuthenticationStrategy authStrategy, final AuthExchange authExchange, final HttpContext context) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); if (LOG.isDebugEnabled()) { LOG.debug("{} {} requested authentication", exchangeId, host.toHostString()); } final Header[] headers = response.getHeaders( challengeType == ChallengeType.PROXY ? HttpHeaders.PROXY_AUTHENTICATE : HttpHeaders.WWW_AUTHENTICATE); final Map challengeMap = new HashMap<>(); for (final Header header: headers) { final CharArrayBuffer buffer; final int pos; if (header instanceof FormattedHeader) { buffer = ((FormattedHeader) header).getBuffer(); pos = ((FormattedHeader) header).getValuePos(); } else { final String s = header.getValue(); if (s == null) { continue; } buffer = new CharArrayBuffer(s.length()); buffer.append(s); pos = 0; } final ParserCursor cursor = new ParserCursor(pos, buffer.length()); final List authChallenges; try { authChallenges = parser.parse(challengeType, buffer, cursor); } catch (final ParseException ex) { if (LOG.isWarnEnabled()) { LOG.warn("{} Malformed challenge: {}", exchangeId, header.getValue()); } continue; } for (final AuthChallenge authChallenge: authChallenges) { final String schemeName = authChallenge.getSchemeName().toLowerCase(Locale.ROOT); if (!challengeMap.containsKey(schemeName)) { challengeMap.put(schemeName, authChallenge); } } } if (challengeMap.isEmpty()) { if (LOG.isDebugEnabled()) { LOG.debug("{} Response contains no valid authentication challenges", exchangeId); } authExchange.reset(); return false; } switch (authExchange.getState()) { case FAILURE: return false; case SUCCESS: authExchange.reset(); break; case CHALLENGED: case HANDSHAKE: Asserts.notNull(authExchange.getAuthScheme(), "AuthScheme"); case UNCHALLENGED: final AuthScheme authScheme = authExchange.getAuthScheme(); if (authScheme != null) { final String schemeName = authScheme.getName(); final AuthChallenge challenge = challengeMap.get(schemeName.toLowerCase(Locale.ROOT)); if (challenge != null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Authorization challenge processed", exchangeId); } try { authScheme.processChallenge(challenge, context); } catch (final MalformedChallengeException ex) { if (LOG.isWarnEnabled()) { LOG.warn("{} {}", exchangeId, ex.getMessage()); } authExchange.reset(); authExchange.setState(AuthExchange.State.FAILURE); return false; } if (authScheme.isChallengeComplete()) { if (LOG.isDebugEnabled()) { LOG.debug("{} Authentication failed", exchangeId); } authExchange.reset(); authExchange.setState(AuthExchange.State.FAILURE); return false; } authExchange.setState(AuthExchange.State.HANDSHAKE); return true; } authExchange.reset(); // Retry authentication with a different scheme } } final List preferredSchemes = authStrategy.select(challengeType, challengeMap, context); final CredentialsProvider credsProvider = clientContext.getCredentialsProvider(); if (credsProvider == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Credentials provider not set in the context", exchangeId); } return false; } final Queue authOptions = new LinkedList<>(); if (LOG.isDebugEnabled()) { LOG.debug("{} Selecting authentication options", exchangeId); } for (final AuthScheme authScheme: preferredSchemes) { try { final String schemeName = authScheme.getName(); final AuthChallenge challenge = challengeMap.get(schemeName.toLowerCase(Locale.ROOT)); authScheme.processChallenge(challenge, context); if (authScheme.isResponseReady(host, credsProvider, context)) { authOptions.add(authScheme); } } catch (final AuthenticationException | MalformedChallengeException ex) { if (LOG.isWarnEnabled()) { LOG.warn(ex.getMessage()); } } } if (!authOptions.isEmpty()) { if (LOG.isDebugEnabled()) { LOG.debug("{} Selected authentication options: {}", exchangeId, authOptions); } authExchange.reset(); authExchange.setState(AuthExchange.State.CHALLENGED); authExchange.setOptions(authOptions); return true; } return false; } /** * Generates a response to the authentication challenge based on the actual {@link AuthExchange} state * and adds it to the given {@link HttpRequest} message . * * @param host the hostname of the opposite endpoint. * @param challengeType the challenge type (target or proxy). * @param request the request message head. * @param authExchange the current authentication exchange state. * @param context the current execution context. */ public void addAuthResponse( final HttpHost host, final ChallengeType challengeType, final HttpRequest request, final AuthExchange authExchange, final HttpContext context) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); AuthScheme authScheme = authExchange.getAuthScheme(); switch (authExchange.getState()) { case FAILURE: return; case SUCCESS: Asserts.notNull(authScheme, "AuthScheme"); if (authScheme.isConnectionBased()) { return; } break; case HANDSHAKE: Asserts.notNull(authScheme, "AuthScheme"); break; case CHALLENGED: final Queue authOptions = authExchange.getAuthOptions(); if (authOptions != null) { while (!authOptions.isEmpty()) { authScheme = authOptions.remove(); authExchange.select(authScheme); if (LOG.isDebugEnabled()) { LOG.debug("{} Generating response to an authentication challenge using {} scheme", exchangeId, authScheme.getName()); } try { final String authResponse = authScheme.generateAuthResponse(host, request, context); final Header header = new BasicHeader( challengeType == ChallengeType.TARGET ? HttpHeaders.AUTHORIZATION : HttpHeaders.PROXY_AUTHORIZATION, authResponse); request.addHeader(header); break; } catch (final AuthenticationException ex) { if (LOG.isWarnEnabled()) { LOG.warn("{} {} authentication error: {}", exchangeId, authScheme, ex.getMessage()); } } } return; } Asserts.notNull(authScheme, "AuthScheme"); default: } if (authScheme != null) { try { final String authResponse = authScheme.generateAuthResponse(host, request, context); final Header header = new BasicHeader( challengeType == ChallengeType.TARGET ? HttpHeaders.AUTHORIZATION : HttpHeaders.PROXY_AUTHORIZATION, authResponse); request.addHeader(header); } catch (final AuthenticationException ex) { if (LOG.isErrorEnabled()) { LOG.error("{} {} authentication error: {}", exchangeId, authScheme, ex.getMessage()); } } } } } HttpEntityDigester.java000066400000000000000000000045231434266521000371510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.io.IOException; import java.io.OutputStream; import java.security.MessageDigest; class HttpEntityDigester extends OutputStream { private final MessageDigest digester; private boolean closed; private byte[] digest; HttpEntityDigester(final MessageDigest digester) { super(); this.digester = digester; this.digester.reset(); } @Override public void write(final int b) throws IOException { if (this.closed) { throw new IOException("Stream has been already closed"); } this.digester.update((byte) b); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { if (this.closed) { throw new IOException("Stream has been already closed"); } this.digester.update(b, off, len); } @Override public void close() throws IOException { if (this.closed) { return; } this.closed = true; this.digest = this.digester.digest(); super.close(); } public byte[] getDigest() { return this.digest; } } KerberosScheme.java000066400000000000000000000046361434266521000362540ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.KerberosConfig; import org.apache.hc.core5.annotation.Experimental; import org.ietf.jgss.GSSException; import org.ietf.jgss.Oid; /** * Kerberos authentication scheme. *

* Please note this class is considered experimental and may be discontinued or removed * in the future. *

* * @since 4.2 */ @Experimental public class KerberosScheme extends GGSSchemeBase { private static final String KERBEROS_OID = "1.2.840.113554.1.2.2"; /** * @since 5.0 */ public KerberosScheme(final KerberosConfig config, final DnsResolver dnsResolver) { super(config, dnsResolver); } public KerberosScheme() { super(); } @Override public String getName() { return StandardAuthScheme.KERBEROS; } @Override protected byte[] generateToken(final byte[] input, final String serviceName, final String authServer) throws GSSException { return generateGSSToken(input, new Oid(KERBEROS_OID), serviceName, authServer); } @Override public boolean isConnectionBased() { return true; } } KerberosSchemeFactory.java000066400000000000000000000053411434266521000375760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.KerberosConfig; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link AuthSchemeFactory} implementation that creates and initializes * {@link KerberosScheme} instances. *

* Please note this class is considered experimental and may be discontinued or removed * in the future. *

* * @since 4.2 */ @Contract(threading = ThreadingBehavior.STATELESS) @Experimental public class KerberosSchemeFactory implements AuthSchemeFactory { /** * Singleton instance for the default configuration. */ public static final KerberosSchemeFactory DEFAULT = new KerberosSchemeFactory(KerberosConfig.DEFAULT, SystemDefaultDnsResolver.INSTANCE); private final KerberosConfig config; private final DnsResolver dnsResolver; /** * @since 5.0 */ public KerberosSchemeFactory(final KerberosConfig config, final DnsResolver dnsResolver) { super(); this.config = config; this.dnsResolver = dnsResolver; } @Override public AuthScheme create(final HttpContext context) { return new KerberosScheme(this.config, this.dnsResolver); } } NTLMEngine.java000066400000000000000000000046501434266521000352470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; /** * Abstract NTLM authentication engine. The engine can be used to * generate Type1 messages and Type3 messages in response to a * Type2 challenge. * * @since 4.0 */ public interface NTLMEngine { /** * Generates a Type1 message given the domain and workstation. * * @param domain Optional Windows domain name. Can be {@code null}. * @param workstation Optional Windows workstation name. Can be * {@code null}. * @return Type1 message * @throws NTLMEngineException */ String generateType1Msg( String domain, String workstation) throws NTLMEngineException; /** * Generates a Type3 message given the user credentials and the * authentication challenge. * * @param username Windows user name * @param password Password * @param domain Windows domain name * @param workstation Windows workstation name * @param challenge Type2 challenge. * @return Type3 response. * @throws NTLMEngineException */ String generateType3Msg( String username, char[] password, String domain, String workstation, String challenge) throws NTLMEngineException; } NTLMEngineException.java000066400000000000000000000042531434266521000371250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.auth.AuthenticationException; /** * Signals NTLM protocol failure. * * @since 4.0 */ public class NTLMEngineException extends AuthenticationException { private static final long serialVersionUID = 6027981323731768824L; public NTLMEngineException() { super(); } /** * Creates a new NTLMEngineException with the specified message. * * @param message the exception detail message */ public NTLMEngineException(final String message) { super(message); } /** * Creates a new NTLMEngineException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public NTLMEngineException(final String message, final Throwable cause) { super(message, cause); } } NTLMEngineImpl.java000066400000000000000000002411521434266521000360710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.Arrays; import java.util.Locale; import java.util.Random; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import org.apache.hc.client5.http.utils.Base64; import org.apache.hc.client5.http.utils.ByteArrayBuilder; /** * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM * authentication protocol. * * @since 4.1 */ final class NTLMEngineImpl implements NTLMEngine { /** Unicode encoding */ private static final Charset UNICODE_LITTLE_UNMARKED = Charset.forName("UnicodeLittleUnmarked"); /** Character encoding */ private static final Charset DEFAULT_CHARSET = StandardCharsets.US_ASCII; // Flags we use; descriptions according to: // http://davenport.sourceforge.net/ntlm.html // and // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx // [MS-NLMP] section 2.2.2.5 static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001; // Unicode string encoding requested static final int FLAG_REQUEST_OEM_ENCODING = 0x00000002; // OEM string encoding requested static final int FLAG_REQUEST_TARGET = 0x00000004; // Requests target field static final int FLAG_REQUEST_SIGN = 0x00000010; // Requests all messages have a signature attached, in NEGOTIATE message. static final int FLAG_REQUEST_SEAL = 0x00000020; // Request key exchange for message confidentiality in NEGOTIATE message. MUST be used in conjunction with 56BIT. static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080; // Request Lan Manager key instead of user session key static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security. MUST be set in NEGOTIATE and CHALLENGE both static final int FLAG_DOMAIN_PRESENT = 0x00001000; // Domain is present in message static final int FLAG_WORKSTATION_PRESENT = 0x00002000; // Workstation is present in message static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000; // Requests a signature block on all messages. Overridden by REQUEST_SIGN and REQUEST_SEAL. static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security static final int FLAG_REQUEST_VERSION = 0x02000000; // Request protocol version static final int FLAG_TARGETINFO_PRESENT = 0x00800000; // From server in challenge message, indicating targetinfo is present static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000; // Request explicit key exchange static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000; // Must be used in conjunction with SEAL // Attribute-value identifiers (AvId) // according to [MS-NLMP] section 2.2.2.1 static final int MSV_AV_EOL = 0x0000; // Indicates that this is the last AV_PAIR in the list. static final int MSV_AV_NB_COMPUTER_NAME = 0x0001; // The server's NetBIOS computer name. static final int MSV_AV_NB_DOMAIN_NAME = 0x0002; // The server's NetBIOS domain name. static final int MSV_AV_DNS_COMPUTER_NAME = 0x0003; // The fully qualified domain name (FQDN) of the computer. static final int MSV_AV_DNS_DOMAIN_NAME = 0x0004; // The FQDN of the domain. static final int MSV_AV_DNS_TREE_NAME = 0x0005; // The FQDN of the forest. static final int MSV_AV_FLAGS = 0x0006; // A 32-bit value indicating server or client configuration. static final int MSV_AV_TIMESTAMP = 0x0007; // server local time static final int MSV_AV_SINGLE_HOST = 0x0008; // A Single_Host_Data structure. static final int MSV_AV_TARGET_NAME = 0x0009; // The SPN of the target server. static final int MSV_AV_CHANNEL_BINDINGS = 0x000A; // A channel bindings hash. static final int MSV_AV_FLAGS_ACCOUNT_AUTH_CONSTAINED = 0x00000001; // Indicates to the client that the account authentication is constrained. static final int MSV_AV_FLAGS_MIC = 0x00000002; // Indicates that the client is providing message integrity in the MIC field in the AUTHENTICATE_MESSAGE. static final int MSV_AV_FLAGS_UNTRUSTED_TARGET_SPN = 0x00000004; // Indicates that the client is providing a target SPN generated from an untrusted source. /** Secure random generator */ private static final java.security.SecureRandom RND_GEN; static { java.security.SecureRandom rnd = null; try { rnd = java.security.SecureRandom.getInstance("SHA1PRNG"); } catch (final Exception ignore) { // ignore } RND_GEN = rnd; } /** The signature string as bytes in the default encoding */ private static final byte[] SIGNATURE = getNullTerminatedAsciiString("NTLMSSP"); // Key derivation magic strings for the SIGNKEY algorithm defined in // [MS-NLMP] section 3.4.5.2ASCII private static final byte[] SIGN_MAGIC_SERVER = getNullTerminatedAsciiString( "session key to server-to-client signing key magic constant"); private static final byte[] SIGN_MAGIC_CLIENT = getNullTerminatedAsciiString( "session key to client-to-server signing key magic constant"); private static final byte[] SEAL_MAGIC_SERVER = getNullTerminatedAsciiString( "session key to server-to-client sealing key magic constant"); private static final byte[] SEAL_MAGIC_CLIENT = getNullTerminatedAsciiString( "session key to client-to-server sealing key magic constant"); // prefix for GSS API channel binding private static final byte[] MAGIC_TLS_SERVER_ENDPOINT = "tls-server-end-point:".getBytes(StandardCharsets.US_ASCII); private static byte[] getNullTerminatedAsciiString( final String source ) { final byte[] bytesWithoutNull = source.getBytes(StandardCharsets.US_ASCII); final byte[] target = new byte[bytesWithoutNull.length + 1]; System.arraycopy(bytesWithoutNull, 0, target, 0, bytesWithoutNull.length); target[bytesWithoutNull.length] = (byte) 0x00; return target; } private static final String TYPE_1_MESSAGE = new Type1Message().getResponse(); NTLMEngineImpl() { } /** * Returns the response for the given message. * * @param message * the message that was received from the server. * @param username * the username to authenticate with. * @param password * the password to authenticate with. * @param host * The host. * @param domain * the NT domain to authenticate in. * @return The response. */ static String getResponseFor(final String message, final String username, final char[] password, final String host, final String domain) throws NTLMEngineException { final String response; if (message == null || message.trim().equals("")) { response = getType1Message(host, domain); } else { final Type2Message t2m = new Type2Message(message); response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m.getFlags(), t2m.getTarget(), t2m.getTargetInfo()); } return response; } /** * Returns the response for the given message. * * @param message * the message that was received from the server. * @param username * the username to authenticate with. * @param password * the password to authenticate with. * @param host * The host. * @param domain * the NT domain to authenticate in. * @return The response. */ static String getResponseFor(final String message, final String username, final char[] password, final String host, final String domain, final Certificate peerServerCertificate) throws NTLMEngineException { final String response; if (message == null || message.trim().equals("")) { response = new Type1Message(host, domain).getResponse(); } else { final Type1Message t1m = new Type1Message(host, domain); final Type2Message t2m = new Type2Message(message); response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m.getFlags(), t2m.getTarget(), t2m.getTargetInfo(), peerServerCertificate, t1m.getBytes(), t2m.getBytes()); } return response; } /** * Creates the first message (type 1 message) in the NTLM authentication * sequence. This message includes the user name, domain and host for the * authentication session. * * @param host * the computer name of the host requesting authentication. * @param domain * The domain to authenticate with. * @return String the message to add to the HTTP request header. */ static String getType1Message(final String host, final String domain) { // For compatibility reason do not include domain and host in type 1 message //return new Type1Message(domain, host).getResponse(); return TYPE_1_MESSAGE; } /** * Creates the type 3 message using the given server nonce. The type 3 * message includes all the information for authentication, host, domain, * username and the result of encrypting the nonce sent by the server using * the user's password as the key. * * @param user * The user name. This should not include the domain name. * @param password * The password. * @param host * The host that is originating the authentication request. * @param domain * The domain to authenticate within. * @param nonce * the 8 byte array the server sent. * @return The type 3 message. * @throws NTLMEngineException * If {@link Type3Message#Type3Message(String, String, String, char[], byte[], int, String, byte[])} fails. */ static String getType3Message(final String user, final char[] password, final String host, final String domain, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation) throws NTLMEngineException { return new Type3Message(domain, host, user, password, nonce, type2Flags, target, targetInformation).getResponse(); } /** * Creates the type 3 message using the given server nonce. The type 3 * message includes all the information for authentication, host, domain, * username and the result of encrypting the nonce sent by the server using * the user's password as the key. * * @param user * The user name. This should not include the domain name. * @param password * The password. * @param host * The host that is originating the authentication request. * @param domain * The domain to authenticate within. * @param nonce * the 8 byte array the server sent. * @return The type 3 message. */ static String getType3Message(final String user, final char[] password, final String host, final String domain, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation, final Certificate peerServerCertificate, final byte[] type1Message, final byte[] type2Message) throws NTLMEngineException { return new Type3Message(domain, host, user, password, nonce, type2Flags, target, targetInformation, peerServerCertificate, type1Message, type2Message).getResponse(); } private static int readULong(final byte[] src, final int index) { if (src.length < index + 4) { return 0; } return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8) | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24); } private static int readUShort(final byte[] src, final int index) { if (src.length < index + 2) { return 0; } return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8); } private static byte[] readSecurityBuffer(final byte[] src, final int index) { final int length = readUShort(src, index); final int offset = readULong(src, index + 4); if (src.length < offset + length) { return new byte[length]; } final byte[] buffer = new byte[length]; System.arraycopy(src, offset, buffer, 0, length); return buffer; } /** Calculate a challenge block */ private static byte[] makeRandomChallenge(final Random random) { final byte[] rval = new byte[8]; synchronized (random) { random.nextBytes(rval); } return rval; } /** Calculate a 16-byte secondary key */ private static byte[] makeSecondaryKey(final Random random) { final byte[] rval = new byte[16]; synchronized (random) { random.nextBytes(rval); } return rval; } static class CipherGen { final Random random; final long currentTime; final String domain; final String user; final char[] password; final byte[] challenge; final String target; final byte[] targetInformation; // Information we can generate but may be passed in (for testing) byte[] clientChallenge; byte[] clientChallenge2; byte[] secondaryKey; byte[] timestamp; // Stuff we always generate byte[] lmHash; byte[] lmResponse; byte[] ntlmHash; byte[] ntlmResponse; byte[] ntlmv2Hash; byte[] lmv2Hash; byte[] lmv2Response; byte[] ntlmv2Blob; byte[] ntlmv2Response; byte[] ntlm2SessionResponse; byte[] lm2SessionResponse; byte[] lmUserSessionKey; byte[] ntlmUserSessionKey; byte[] ntlmv2UserSessionKey; byte[] ntlm2SessionResponseUserSessionKey; byte[] lanManagerSessionKey; public CipherGen(final Random random, final long currentTime, final String domain, final String user, final char[] password, final byte[] challenge, final String target, final byte[] targetInformation, final byte[] clientChallenge, final byte[] clientChallenge2, final byte[] secondaryKey, final byte[] timestamp) { this.random = random; this.currentTime = currentTime; this.domain = domain; this.target = target; this.user = user; this.password = password; this.challenge = challenge; this.targetInformation = targetInformation; this.clientChallenge = clientChallenge; this.clientChallenge2 = clientChallenge2; this.secondaryKey = secondaryKey; this.timestamp = timestamp; } public CipherGen(final Random random, final long currentTime, final String domain, final String user, final char[] password, final byte[] challenge, final String target, final byte[] targetInformation) { this(random, currentTime, domain, user, password, challenge, target, targetInformation, null, null, null, null); } /** Calculate and return client challenge */ public byte[] getClientChallenge() { if (clientChallenge == null) { clientChallenge = makeRandomChallenge(random); } return clientChallenge; } /** Calculate and return second client challenge */ public byte[] getClientChallenge2() { if (clientChallenge2 == null) { clientChallenge2 = makeRandomChallenge(random); } return clientChallenge2; } /** Calculate and return random secondary key */ public byte[] getSecondaryKey() { if (secondaryKey == null) { secondaryKey = makeSecondaryKey(random); } return secondaryKey; } /** Calculate and return the LMHash */ public byte[] getLMHash() throws NTLMEngineException { if (lmHash == null) { lmHash = lmHash(password); } return lmHash; } /** Calculate and return the LMResponse */ public byte[] getLMResponse() throws NTLMEngineException { if (lmResponse == null) { lmResponse = lmResponse(getLMHash(),challenge); } return lmResponse; } /** Calculate and return the NTLMHash */ public byte[] getNTLMHash() throws NTLMEngineException { if (ntlmHash == null) { ntlmHash = ntlmHash(password); } return ntlmHash; } /** Calculate and return the NTLMResponse */ public byte[] getNTLMResponse() throws NTLMEngineException { if (ntlmResponse == null) { ntlmResponse = lmResponse(getNTLMHash(),challenge); } return ntlmResponse; } /** Calculate the LMv2 hash */ public byte[] getLMv2Hash() throws NTLMEngineException { if (lmv2Hash == null) { lmv2Hash = lmv2Hash(domain, user, getNTLMHash()); } return lmv2Hash; } /** Calculate the NTLMv2 hash */ public byte[] getNTLMv2Hash() throws NTLMEngineException { if (ntlmv2Hash == null) { ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash()); } return ntlmv2Hash; } /** Calculate a timestamp */ public byte[] getTimestamp() { if (timestamp == null) { long time = this.currentTime; time += 11644473600000L; // milliseconds from January 1, 1601 -> epoch. time *= 10000; // tenths of a microsecond. // convert to little-endian byte array. timestamp = new byte[8]; for (int i = 0; i < 8; i++) { timestamp[i] = (byte) time; time >>>= 8; } } return timestamp; } /** Calculate the NTLMv2Blob */ public byte[] getNTLMv2Blob() { if (ntlmv2Blob == null) { ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp()); } return ntlmv2Blob; } /** Calculate the NTLMv2Response */ public byte[] getNTLMv2Response() throws NTLMEngineException { if (ntlmv2Response == null) { ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob()); } return ntlmv2Response; } /** Calculate the LMv2Response */ public byte[] getLMv2Response() throws NTLMEngineException { if (lmv2Response == null) { lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge()); } return lmv2Response; } /** Get NTLM2SessionResponse */ public byte[] getNTLM2SessionResponse() throws NTLMEngineException { if (ntlm2SessionResponse == null) { ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge()); } return ntlm2SessionResponse; } /** Calculate and return LM2 session response */ public byte[] getLM2SessionResponse() { if (lm2SessionResponse == null) { final byte[] clntChallenge = getClientChallenge(); lm2SessionResponse = new byte[24]; System.arraycopy(clntChallenge, 0, lm2SessionResponse, 0, clntChallenge.length); Arrays.fill(lm2SessionResponse, clntChallenge.length, lm2SessionResponse.length, (byte) 0x00); } return lm2SessionResponse; } /** Get LMUserSessionKey */ public byte[] getLMUserSessionKey() throws NTLMEngineException { if (lmUserSessionKey == null) { lmUserSessionKey = new byte[16]; System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8); Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00); } return lmUserSessionKey; } /** Get NTLMUserSessionKey */ public byte[] getNTLMUserSessionKey() throws NTLMEngineException { if (ntlmUserSessionKey == null) { final MD4 md4 = new MD4(); md4.update(getNTLMHash()); ntlmUserSessionKey = md4.getOutput(); } return ntlmUserSessionKey; } /** GetNTLMv2UserSessionKey */ public byte[] getNTLMv2UserSessionKey() throws NTLMEngineException { if (ntlmv2UserSessionKey == null) { final byte[] ntlmv2hash = getNTLMv2Hash(); final byte[] truncatedResponse = new byte[16]; System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16); ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash); } return ntlmv2UserSessionKey; } /** Get NTLM2SessionResponseUserSessionKey */ public byte[] getNTLM2SessionResponseUserSessionKey() throws NTLMEngineException { if (ntlm2SessionResponseUserSessionKey == null) { final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse(); final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length]; System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length); System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length); ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,getNTLMUserSessionKey()); } return ntlm2SessionResponseUserSessionKey; } /** Get LAN Manager session key */ public byte[] getLanManagerSessionKey() throws NTLMEngineException { if (lanManagerSessionKey == null) { try { final byte[] keyBytes = new byte[14]; System.arraycopy(getLMHash(), 0, keyBytes, 0, 8); Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd); final Key lowKey = createDESKey(keyBytes, 0); final Key highKey = createDESKey(keyBytes, 7); final byte[] truncatedResponse = new byte[8]; System.arraycopy(getLMResponse(), 0, truncatedResponse, 0, truncatedResponse.length); Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); des.init(Cipher.ENCRYPT_MODE, lowKey); final byte[] lowPart = des.doFinal(truncatedResponse); des = Cipher.getInstance("DES/ECB/NoPadding"); des.init(Cipher.ENCRYPT_MODE, highKey); final byte[] highPart = des.doFinal(truncatedResponse); lanManagerSessionKey = new byte[16]; System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length); System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length); } catch (final Exception e) { throw new NTLMEngineException(e.getMessage(), e); } } return lanManagerSessionKey; } } /** Calculates HMAC-MD5 */ static byte[] hmacMD5(final byte[] value, final byte[] key) { final HMACMD5 hmacMD5 = new HMACMD5(key); hmacMD5.update(value); return hmacMD5.getOutput(); } /** Calculates RC4 */ static byte[] RC4(final byte[] value, final byte[] key) throws NTLMEngineException { try { final Cipher rc4 = Cipher.getInstance("RC4"); rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4")); return rc4.doFinal(value); } catch (final Exception e) { throw new NTLMEngineException(e.getMessage(), e); } } /** * Calculates the NTLM2 Session Response for the given challenge, using the * specified password and client challenge. * * @return The NTLM2 Session Response. This is placed in the NTLM response * field of the Type 3 message; the LM response field contains the * client challenge, null-padded to 24 bytes. */ static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge, final byte[] clientChallenge) throws NTLMEngineException { try { final MessageDigest md5 = getMD5(); md5.update(challenge); md5.update(clientChallenge); final byte[] digest = md5.digest(); final byte[] sessionHash = new byte[8]; System.arraycopy(digest, 0, sessionHash, 0, 8); return lmResponse(ntlmHash, sessionHash); } catch (final Exception e) { if (e instanceof NTLMEngineException) { throw (NTLMEngineException) e; } throw new NTLMEngineException(e.getMessage(), e); } } /** * Creates the LM Hash of the user's password. * * @param password * The password. * * @return The LM Hash of the given password, used in the calculation of the * LM Response. */ private static byte[] lmHash(final char[] password) throws NTLMEngineException { try { final char[] tmp = new char[password.length]; for (int i = 0; i < password.length; i++) { tmp[i] = Character.toUpperCase(password[i]); } final byte[] oemPassword = new ByteArrayBuilder().append(tmp).toByteArray(); final int length = Math.min(oemPassword.length, 14); final byte[] keyBytes = new byte[14]; System.arraycopy(oemPassword, 0, keyBytes, 0, length); final Key lowKey = createDESKey(keyBytes, 0); final Key highKey = createDESKey(keyBytes, 7); final byte[] magicConstant = "KGS!@#$%".getBytes(StandardCharsets.US_ASCII); final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); des.init(Cipher.ENCRYPT_MODE, lowKey); final byte[] lowHash = des.doFinal(magicConstant); des.init(Cipher.ENCRYPT_MODE, highKey); final byte[] highHash = des.doFinal(magicConstant); final byte[] lmHash = new byte[16]; System.arraycopy(lowHash, 0, lmHash, 0, 8); System.arraycopy(highHash, 0, lmHash, 8, 8); return lmHash; } catch (final Exception e) { throw new NTLMEngineException(e.getMessage(), e); } } /** * Creates the NTLM Hash of the user's password. * * @param password * The password. * * @return The NTLM Hash of the given password, used in the calculation of * the NTLM Response and the NTLMv2 and LMv2 Hashes. */ private static byte[] ntlmHash(final char[] password) throws NTLMEngineException { final byte[] unicodePassword = new ByteArrayBuilder() .charset(UNICODE_LITTLE_UNMARKED).append(password).toByteArray(); final MD4 md4 = new MD4(); md4.update(unicodePassword); return md4.getOutput(); } /** * Creates the LMv2 Hash of the user's password. * * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2 * Responses. */ private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash) throws NTLMEngineException { final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); // Upper case username, upper case domain! hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED)); if (domain != null) { hmacMD5.update(domain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED)); } return hmacMD5.getOutput(); } /** * Creates the NTLMv2 Hash of the user's password. * * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2 * Responses. */ private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash) throws NTLMEngineException { final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash); // Upper case username, mixed case target!! hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED)); if (domain != null) { hmacMD5.update(domain.getBytes(UNICODE_LITTLE_UNMARKED)); } return hmacMD5.getOutput(); } /** * Creates the LM Response from the given hash and Type 2 challenge. * * @param hash * The LM or NTLM Hash. * @param challenge * The server challenge from the Type 2 message. * * @return The response (either LM or NTLM, depending on the provided hash). */ private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException { try { final byte[] keyBytes = new byte[21]; System.arraycopy(hash, 0, keyBytes, 0, 16); final Key lowKey = createDESKey(keyBytes, 0); final Key middleKey = createDESKey(keyBytes, 7); final Key highKey = createDESKey(keyBytes, 14); final Cipher des = Cipher.getInstance("DES/ECB/NoPadding"); des.init(Cipher.ENCRYPT_MODE, lowKey); final byte[] lowResponse = des.doFinal(challenge); des.init(Cipher.ENCRYPT_MODE, middleKey); final byte[] middleResponse = des.doFinal(challenge); des.init(Cipher.ENCRYPT_MODE, highKey); final byte[] highResponse = des.doFinal(challenge); final byte[] lmResponse = new byte[24]; System.arraycopy(lowResponse, 0, lmResponse, 0, 8); System.arraycopy(middleResponse, 0, lmResponse, 8, 8); System.arraycopy(highResponse, 0, lmResponse, 16, 8); return lmResponse; } catch (final Exception e) { throw new NTLMEngineException(e.getMessage(), e); } } /** * Creates the LMv2 Response from the given hash, client data, and Type 2 * challenge. * * @param hash * The NTLMv2 Hash. * @param clientData * The client data (blob or client challenge). * @param challenge * The server challenge from the Type 2 message. * * @return The response (either NTLMv2 or LMv2, depending on the client * data). */ private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData) { final HMACMD5 hmacMD5 = new HMACMD5(hash); hmacMD5.update(challenge); hmacMD5.update(clientData); final byte[] mac = hmacMD5.getOutput(); final byte[] lmv2Response = new byte[mac.length + clientData.length]; System.arraycopy(mac, 0, lmv2Response, 0, mac.length); System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length); return lmv2Response; } enum Mode { CLIENT, SERVER } static class Handle { private final byte[] signingKey; private byte[] sealingKey; private final Cipher rc4; final Mode mode; final private boolean isConnection; int sequenceNumber; Handle( final byte[] exportedSessionKey, final Mode mode, final boolean isConnection ) throws NTLMEngineException { this.isConnection = isConnection; this.mode = mode; try { final MessageDigest signMd5 = getMD5(); final MessageDigest sealMd5 = getMD5(); signMd5.update( exportedSessionKey ); sealMd5.update( exportedSessionKey ); if ( mode == Mode.CLIENT ) { signMd5.update( SIGN_MAGIC_CLIENT ); sealMd5.update( SEAL_MAGIC_CLIENT ); } else { signMd5.update( SIGN_MAGIC_SERVER ); sealMd5.update( SEAL_MAGIC_SERVER ); } signingKey = signMd5.digest(); sealingKey = sealMd5.digest(); } catch ( final Exception e ) { throw new NTLMEngineException( e.getMessage(), e ); } rc4 = initCipher(); } public byte[] getSigningKey() { return signingKey; } public byte[] getSealingKey() { return sealingKey; } private Cipher initCipher() throws NTLMEngineException { final Cipher cipher; try { cipher = Cipher.getInstance( "RC4" ); if ( mode == Mode.CLIENT ) { cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) ); } else { cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) ); } } catch ( final Exception e ) { throw new NTLMEngineException( e.getMessage(), e ); } return cipher; } private void advanceMessageSequence() throws NTLMEngineException { if ( !isConnection ) { final MessageDigest sealMd5 = getMD5(); sealMd5.update( sealingKey ); final byte[] seqNumBytes = new byte[4]; writeULong( seqNumBytes, sequenceNumber, 0 ); sealMd5.update( seqNumBytes ); sealingKey = sealMd5.digest(); initCipher(); } sequenceNumber++; } private byte[] encrypt( final byte[] data ) { return rc4.update( data ); } private byte[] decrypt( final byte[] data ) { return rc4.update( data ); } private byte[] computeSignature( final byte[] message ) { final byte[] sig = new byte[16]; // version sig[0] = 0x01; sig[1] = 0x00; sig[2] = 0x00; sig[3] = 0x00; // HMAC (first 8 bytes) final HMACMD5 hmacMD5 = new HMACMD5( signingKey ); hmacMD5.update( encodeLong( sequenceNumber ) ); hmacMD5.update( message ); final byte[] hmac = hmacMD5.getOutput(); final byte[] trimmedHmac = new byte[8]; System.arraycopy( hmac, 0, trimmedHmac, 0, 8 ); final byte[] encryptedHmac = encrypt( trimmedHmac ); System.arraycopy( encryptedHmac, 0, sig, 4, 8 ); // sequence number encodeLong( sig, 12, sequenceNumber ); return sig; } private boolean validateSignature( final byte[] signature, final byte[] message ) { final byte[] computedSignature = computeSignature( message ); // log.info( "SSSSS validateSignature("+seqNumber+")\n" // + " received: " + DebugUtil.dump( signature ) + "\n" // + " computed: " + DebugUtil.dump( computedSignature ) ); return Arrays.equals( signature, computedSignature ); } public byte[] signAndEncryptMessage( final byte[] cleartextMessage ) throws NTLMEngineException { final byte[] encryptedMessage = encrypt( cleartextMessage ); final byte[] signature = computeSignature( cleartextMessage ); final byte[] outMessage = new byte[signature.length + encryptedMessage.length]; System.arraycopy( signature, 0, outMessage, 0, signature.length ); System.arraycopy( encryptedMessage, 0, outMessage, signature.length, encryptedMessage.length ); advanceMessageSequence(); return outMessage; } public byte[] decryptAndVerifySignedMessage( final byte[] inMessage ) throws NTLMEngineException { final byte[] signature = new byte[16]; System.arraycopy( inMessage, 0, signature, 0, signature.length ); final byte[] encryptedMessage = new byte[inMessage.length - 16]; System.arraycopy( inMessage, 16, encryptedMessage, 0, encryptedMessage.length ); final byte[] cleartextMessage = decrypt( encryptedMessage ); if ( !validateSignature( signature, cleartextMessage ) ) { throw new NTLMEngineException( "Wrong signature" ); } advanceMessageSequence(); return cleartextMessage; } } private static byte[] encodeLong( final int value ) { final byte[] enc = new byte[4]; encodeLong( enc, 0, value ); return enc; } private static void encodeLong( final byte[] buf, final int offset, final int value ) { buf[offset + 0] = ( byte ) ( value & 0xff ); buf[offset + 1] = ( byte ) ( value >> 8 & 0xff ); buf[offset + 2] = ( byte ) ( value >> 16 & 0xff ); buf[offset + 3] = ( byte ) ( value >> 24 & 0xff ); } /** * Creates the NTLMv2 blob from the given target information block and * client challenge. * * @param targetInformation * The target information block from the Type 2 message. * @param clientChallenge * The random 8-byte client challenge. * * @return The blob, used in the calculation of the NTLMv2 Response. */ private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) { final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 }; final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8 + unknown1.length + targetInformation.length + unknown2.length]; int offset = 0; System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length); offset += blobSignature.length; System.arraycopy(reserved, 0, blob, offset, reserved.length); offset += reserved.length; System.arraycopy(timestamp, 0, blob, offset, timestamp.length); offset += timestamp.length; System.arraycopy(clientChallenge, 0, blob, offset, 8); offset += 8; System.arraycopy(unknown1, 0, blob, offset, unknown1.length); offset += unknown1.length; System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length); offset += targetInformation.length; System.arraycopy(unknown2, 0, blob, offset, unknown2.length); offset += unknown2.length; return blob; } /** * Creates a DES encryption key from the given key material. * * @param bytes * A byte array containing the DES key material. * @param offset * The offset in the given byte array at which the 7-byte key * material starts. * * @return A DES encryption key created from the key material starting at * the specified offset in the given byte array. */ private static Key createDESKey(final byte[] bytes, final int offset) { final byte[] keyBytes = new byte[7]; System.arraycopy(bytes, offset, keyBytes, 0, 7); final byte[] material = new byte[8]; material[0] = keyBytes[0]; material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1); material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2); material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3); material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4); material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5); material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6); material[7] = (byte) (keyBytes[6] << 1); oddParity(material); return new SecretKeySpec(material, "DES"); } /** * Applies odd parity to the given byte array. * * @param bytes * The data whose parity bits are to be adjusted for odd parity. */ private static void oddParity(final byte[] bytes) { for (int i = 0; i < bytes.length; i++) { final byte b = bytes[i]; final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0; if (needsParity) { bytes[i] |= (byte) 0x01; } else { bytes[i] &= (byte) 0xfe; } } } /** * Find the character set based on the flags. * @param flags is the flags. * @return the character set. */ private static Charset getCharset(final int flags) throws NTLMEngineException { if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) { return DEFAULT_CHARSET; } return UNICODE_LITTLE_UNMARKED; } /** NTLM message generation, base class */ static class NTLMMessage { /** The current response */ byte[] messageContents; /** The current output position */ int currentOutputPosition; /** Constructor to use when message contents are not yet known */ NTLMMessage() { } /** Constructor taking a string */ NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException { this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)), expectedType); } /** Constructor to use when message bytes are known */ NTLMMessage(final byte[] message, final int expectedType) throws NTLMEngineException { messageContents = message; // Look for NTLM message if (messageContents.length < SIGNATURE.length) { throw new NTLMEngineException("NTLM message decoding error - packet too short"); } int i = 0; while (i < SIGNATURE.length) { if (messageContents[i] != SIGNATURE[i]) { throw new NTLMEngineException( "NTLM message expected - instead got unrecognized bytes"); } i++; } // Check to be sure there's a type 2 message indicator next final int type = readULong(SIGNATURE.length); if (type != expectedType) { throw new NTLMEngineException("NTLM type " + expectedType + " message expected - instead got type " + type); } currentOutputPosition = messageContents.length; } /** * Get the length of the signature and flags, so calculations can adjust * offsets accordingly. */ int getPreambleLength() { return SIGNATURE.length + 4; } /** Get the message length */ int getMessageLength() { return currentOutputPosition; } /** Read a byte from a position within the message buffer */ byte readByte(final int position) throws NTLMEngineException { if (messageContents.length < position + 1) { throw new NTLMEngineException("NTLM: Message too short"); } return messageContents[position]; } /** Read a bunch of bytes from a position in the message buffer */ void readBytes(final byte[] buffer, final int position) throws NTLMEngineException { if (messageContents.length < position + buffer.length) { throw new NTLMEngineException("NTLM: Message too short"); } System.arraycopy(messageContents, position, buffer, 0, buffer.length); } /** Read a ushort from a position within the message buffer */ int readUShort(final int position) { return NTLMEngineImpl.readUShort(messageContents, position); } /** Read a ulong from a position within the message buffer */ int readULong(final int position) { return NTLMEngineImpl.readULong(messageContents, position); } /** Read a security buffer from a position within the message buffer */ byte[] readSecurityBuffer(final int position) { return NTLMEngineImpl.readSecurityBuffer(messageContents, position); } /** * Prepares the object to create a response of the given length. * * @param maxlength * the maximum length of the response to prepare, * including the type and the signature (which this method * adds). */ void prepareResponse(final int maxlength, final int messageType) { messageContents = new byte[maxlength]; currentOutputPosition = 0; addBytes(SIGNATURE); addULong(messageType); } /** * Adds the given byte to the response. * * @param b * the byte to add. */ void addByte(final byte b) { messageContents[currentOutputPosition] = b; currentOutputPosition++; } /** * Adds the given bytes to the response. * * @param bytes * the bytes to add. */ void addBytes(final byte[] bytes) { if (bytes == null) { return; } for (final byte b : bytes) { messageContents[currentOutputPosition] = b; currentOutputPosition++; } } /** Adds a USHORT to the response */ void addUShort(final int value) { addByte((byte) (value & 0xff)); addByte((byte) (value >> 8 & 0xff)); } /** Adds a ULong to the response */ void addULong(final int value) { addByte((byte) (value & 0xff)); addByte((byte) (value >> 8 & 0xff)); addByte((byte) (value >> 16 & 0xff)); addByte((byte) (value >> 24 & 0xff)); } /** * Returns the response that has been generated after shrinking the * array if required and base64 encodes the response. * * @return The response as above. */ public String getResponse() { return new String(Base64.encodeBase64(getBytes()), StandardCharsets.US_ASCII); } public byte[] getBytes() { if (messageContents == null) { buildMessage(); } if (messageContents.length > currentOutputPosition) { final byte[] tmp = new byte[currentOutputPosition]; System.arraycopy( messageContents, 0, tmp, 0, currentOutputPosition ); messageContents = tmp; } return messageContents; } void buildMessage() { throw new RuntimeException("Message builder not implemented for "+getClass().getName()); } } /** Type 1 message assembly class */ static class Type1Message extends NTLMMessage { private final byte[] hostBytes; private final byte[] domainBytes; private final int flags; Type1Message(final String domain, final String host) { this(domain, host, null); } Type1Message(final String domain, final String host, final Integer flags) { super(); this.flags = ((flags == null)?getDefaultFlags(): flags.intValue()); // See HTTPCLIENT-1662 final String unqualifiedHost = host; final String unqualifiedDomain = domain; hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null; domainBytes = unqualifiedDomain != null ? unqualifiedDomain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null; } Type1Message() { super(); hostBytes = null; domainBytes = null; flags = getDefaultFlags(); } private int getDefaultFlags() { return //FLAG_WORKSTATION_PRESENT | //FLAG_DOMAIN_PRESENT | // Required flags //FLAG_REQUEST_LAN_MANAGER_KEY | FLAG_REQUEST_NTLMv1 | FLAG_REQUEST_NTLM2_SESSION | // Protocol version request FLAG_REQUEST_VERSION | // Recommended privacy settings FLAG_REQUEST_ALWAYS_SIGN | //FLAG_REQUEST_SEAL | //FLAG_REQUEST_SIGN | // These must be set according to documentation, based on use of SEAL above FLAG_REQUEST_128BIT_KEY_EXCH | FLAG_REQUEST_56BIT_ENCRYPTION | //FLAG_REQUEST_EXPLICIT_KEY_EXCH | FLAG_REQUEST_UNICODE_ENCODING; } /** * Getting the response involves building the message before returning * it */ @Override void buildMessage() { int domainBytesLength = 0; if ( domainBytes != null ) { domainBytesLength = domainBytes.length; } int hostBytesLength = 0; if ( hostBytes != null ) { hostBytesLength = hostBytes.length; } // Now, build the message. Calculate its length first, including // signature or type. final int finalLength = 32 + 8 + hostBytesLength + domainBytesLength; // Set up the response. This will initialize the signature, message // type, and flags. prepareResponse(finalLength, 1); // Flags. These are the complete set of flags we support. addULong(flags); // Domain length (two times). addUShort(domainBytesLength); addUShort(domainBytesLength); // Domain offset. addULong(hostBytesLength + 32 + 8); // Host length (two times). addUShort(hostBytesLength); addUShort(hostBytesLength); // Host offset (always 32 + 8). addULong(32 + 8); // Version addUShort(0x0105); // Build addULong(2600); // NTLM revision addUShort(0x0f00); // Host (workstation) String. if (hostBytes != null) { addBytes(hostBytes); } // Domain String. if (domainBytes != null) { addBytes(domainBytes); } } } /** Type 2 message class */ static class Type2Message extends NTLMMessage { final byte[] challenge; String target; byte[] targetInfo; final int flags; Type2Message(final String messageBody) throws NTLMEngineException { this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET))); } Type2Message(final byte[] message) throws NTLMEngineException { super(message, 2); // Type 2 message is laid out as follows: // First 8 bytes: NTLMSSP[0] // Next 4 bytes: Ulong, value 2 // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset) // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235 // Next 8 bytes, starting at offset 24: Challenge // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros) // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset) // Next 2 bytes, major/minor version number (e.g. 0x05 0x02) // Next 8 bytes, build number // Next 2 bytes, protocol version number (e.g. 0x00 0x0f) // Next, various text fields, and a ushort of value 0 at the end // Parse out the rest of the info we need from the message // The nonce is the 8 bytes starting from the byte in position 24. challenge = new byte[8]; readBytes(challenge, 24); flags = readULong(20); // Do the target! target = null; // The TARGET_DESIRED flag is said to not have understood semantics // in Type2 messages, so use the length of the packet to decide // how to proceed instead if (getMessageLength() >= 12 + 8) { final byte[] bytes = readSecurityBuffer(12); if (bytes.length != 0) { target = new String(bytes, getCharset(flags)); } } // Do the target info! targetInfo = null; // TARGET_DESIRED flag cannot be relied on, so use packet length if (getMessageLength() >= 40 + 8) { final byte[] bytes = readSecurityBuffer(40); if (bytes.length != 0) { targetInfo = bytes; } } } /** Retrieve the challenge */ byte[] getChallenge() { return challenge; } /** Retrieve the target */ String getTarget() { return target; } /** Retrieve the target info */ byte[] getTargetInfo() { return targetInfo; } /** Retrieve the response flags */ int getFlags() { return flags; } } /** Type 3 message assembly class */ static class Type3Message extends NTLMMessage { // For mic computation final byte[] type1Message; final byte[] type2Message; // Response flags from the type2 message final int type2Flags; final byte[] domainBytes; final byte[] hostBytes; final byte[] userBytes; byte[] lmResp; byte[] ntResp; final byte[] sessionKey; final byte[] exportedSessionKey; final boolean computeMic; /** More primitive constructor: don't include cert or previous messages. */ Type3Message(final String domain, final String host, final String user, final char[] password, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation) throws NTLMEngineException { this(domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null); } /** More primitive constructor: don't include cert or previous messages. */ Type3Message(final Random random, final long currentTime, final String domain, final String host, final String user, final char[] password, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation) throws NTLMEngineException { this(random, currentTime, domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null); } /** Constructor. Pass the arguments we will need */ Type3Message(final String domain, final String host, final String user, final char[] password, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation, final Certificate peerServerCertificate, final byte[] type1Message, final byte[] type2Message) throws NTLMEngineException { this(RND_GEN, System.currentTimeMillis(), domain, host, user, password, nonce, type2Flags, target, targetInformation, peerServerCertificate, type1Message, type2Message); } /** Constructor. Pass the arguments we will need */ Type3Message(final Random random, final long currentTime, final String domain, final String host, final String user, final char[] password, final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation, final Certificate peerServerCertificate, final byte[] type1Message, final byte[] type2Message) throws NTLMEngineException { if (random == null) { throw new NTLMEngineException("Random generator not available"); } // Save the flags this.type2Flags = type2Flags; this.type1Message = type1Message; this.type2Message = type2Message; // All host name manipulations now take place in the credentials final String unqualifiedHost = host; // All domain name manipulations now take place in the credentials final String unqualifiedDomain = domain; byte[] responseTargetInformation = targetInformation; if (peerServerCertificate != null) { responseTargetInformation = addGssMicAvsToTargetInfo(targetInformation, peerServerCertificate); computeMic = true; } else { computeMic = false; } // Create a cipher generator class. Use domain BEFORE it gets modified! final CipherGen gen = new CipherGen(random, currentTime, unqualifiedDomain, user, password, nonce, target, responseTargetInformation); // Use the new code to calculate the responses, including v2 if that // seems warranted. byte[] userSessionKey; try { // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet // been tested if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) && targetInformation != null && target != null) { // NTLMv2 ntResp = gen.getNTLMv2Response(); lmResp = gen.getLMv2Response(); if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { userSessionKey = gen.getLanManagerSessionKey(); } else { userSessionKey = gen.getNTLMv2UserSessionKey(); } } else { // NTLMv1 if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) { // NTLM2 session stuff is requested ntResp = gen.getNTLM2SessionResponse(); lmResp = gen.getLM2SessionResponse(); if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { userSessionKey = gen.getLanManagerSessionKey(); } else { userSessionKey = gen.getNTLM2SessionResponseUserSessionKey(); } } else { ntResp = gen.getNTLMResponse(); lmResp = gen.getLMResponse(); if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { userSessionKey = gen.getLanManagerSessionKey(); } else { userSessionKey = gen.getNTLMUserSessionKey(); } } } } catch (final NTLMEngineException e) { // This likely means we couldn't find the MD4 hash algorithm - // fail back to just using LM ntResp = new byte[0]; lmResp = gen.getLMResponse(); if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) { userSessionKey = gen.getLanManagerSessionKey(); } else { userSessionKey = gen.getLMUserSessionKey(); } } if ((type2Flags & FLAG_REQUEST_SIGN) != 0) { if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) { exportedSessionKey = gen.getSecondaryKey(); sessionKey = RC4(exportedSessionKey, userSessionKey); } else { sessionKey = userSessionKey; exportedSessionKey = sessionKey; } } else { if (computeMic) { throw new NTLMEngineException("Cannot sign/seal: no exported session key"); } sessionKey = null; exportedSessionKey = null; } final Charset charset = getCharset(type2Flags); hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(charset) : null; domainBytes = unqualifiedDomain != null ? unqualifiedDomain .toUpperCase(Locale.ROOT).getBytes(charset) : null; userBytes = user.getBytes(charset); } public byte[] getEncryptedRandomSessionKey() { return sessionKey; } public byte[] getExportedSessionKey() { return exportedSessionKey; } /** Assemble the response */ @Override void buildMessage() { final int ntRespLen = ntResp.length; final int lmRespLen = lmResp.length; final int domainLen = domainBytes != null ? domainBytes.length : 0; final int hostLen = hostBytes != null ? hostBytes.length: 0; final int userLen = userBytes.length; final int sessionKeyLen; if (sessionKey != null) { sessionKeyLen = sessionKey.length; } else { sessionKeyLen = 0; } // Calculate the layout within the packet final int lmRespOffset = 72 + // allocate space for the version ( computeMic ? 16 : 0 ); // and MIC final int ntRespOffset = lmRespOffset + lmRespLen; final int domainOffset = ntRespOffset + ntRespLen; final int userOffset = domainOffset + domainLen; final int hostOffset = userOffset + userLen; final int sessionKeyOffset = hostOffset + hostLen; final int finalLength = sessionKeyOffset + sessionKeyLen; // Start the response. Length includes signature and type prepareResponse(finalLength, 3); // LM Resp Length (twice) addUShort(lmRespLen); addUShort(lmRespLen); // LM Resp Offset addULong(lmRespOffset); // NT Resp Length (twice) addUShort(ntRespLen); addUShort(ntRespLen); // NT Resp Offset addULong(ntRespOffset); // Domain length (twice) addUShort(domainLen); addUShort(domainLen); // Domain offset. addULong(domainOffset); // User Length (twice) addUShort(userLen); addUShort(userLen); // User offset addULong(userOffset); // Host length (twice) addUShort(hostLen); addUShort(hostLen); // Host offset addULong(hostOffset); // Session key length (twice) addUShort(sessionKeyLen); addUShort(sessionKeyLen); // Session key offset addULong(sessionKeyOffset); // Flags. addULong( /* //FLAG_WORKSTATION_PRESENT | //FLAG_DOMAIN_PRESENT | // Required flags (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) | (type2Flags & FLAG_REQUEST_NTLMv1) | (type2Flags & FLAG_REQUEST_NTLM2_SESSION) | // Protocol version request FLAG_REQUEST_VERSION | // Recommended privacy settings (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) | (type2Flags & FLAG_REQUEST_SEAL) | (type2Flags & FLAG_REQUEST_SIGN) | // These must be set according to documentation, based on use of SEAL above (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) | (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) | (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) | (type2Flags & FLAG_TARGETINFO_PRESENT) | (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) | (type2Flags & FLAG_REQUEST_TARGET) */ type2Flags ); // Version addUShort(0x0105); // Build addULong(2600); // NTLM revision addUShort(0x0f00); int micPosition = -1; if ( computeMic ) { micPosition = currentOutputPosition; currentOutputPosition += 16; } // Add the actual data addBytes(lmResp); addBytes(ntResp); addBytes(domainBytes); addBytes(userBytes); addBytes(hostBytes); if (sessionKey != null) { addBytes(sessionKey); } // Write the mic back into its slot in the message if (computeMic) { // Computation of message integrity code (MIC) as specified in [MS-NLMP] section 3.2.5.1.2. final HMACMD5 hmacMD5 = new HMACMD5( exportedSessionKey ); hmacMD5.update( type1Message ); hmacMD5.update( type2Message ); hmacMD5.update( messageContents ); final byte[] mic = hmacMD5.getOutput(); System.arraycopy( mic, 0, messageContents, micPosition, mic.length ); } } /** * Add GSS channel binding hash and MIC flag to the targetInfo. * Looks like this is needed if we want to use exported session key for GSS wrapping. */ private byte[] addGssMicAvsToTargetInfo( final byte[] originalTargetInfo, final Certificate peerServerCertificate ) throws NTLMEngineException { final byte[] newTargetInfo = new byte[originalTargetInfo.length + 8 + 20]; final int appendLength = originalTargetInfo.length - 4; // last tag is MSV_AV_EOL, do not copy that System.arraycopy( originalTargetInfo, 0, newTargetInfo, 0, appendLength ); writeUShort( newTargetInfo, MSV_AV_FLAGS, appendLength ); writeUShort( newTargetInfo, 4, appendLength + 2 ); writeULong( newTargetInfo, MSV_AV_FLAGS_MIC, appendLength + 4 ); writeUShort( newTargetInfo, MSV_AV_CHANNEL_BINDINGS, appendLength + 8 ); writeUShort( newTargetInfo, 16, appendLength + 10 ); final byte[] channelBindingsHash; try { final byte[] certBytes = peerServerCertificate.getEncoded(); final MessageDigest sha256 = MessageDigest.getInstance( "SHA-256" ); final byte[] certHashBytes = sha256.digest( certBytes ); final byte[] channelBindingStruct = new byte[16 + 4 + MAGIC_TLS_SERVER_ENDPOINT.length + certHashBytes.length]; writeULong( channelBindingStruct, 0x00000035, 16 ); System.arraycopy( MAGIC_TLS_SERVER_ENDPOINT, 0, channelBindingStruct, 20, MAGIC_TLS_SERVER_ENDPOINT.length ); System.arraycopy( certHashBytes, 0, channelBindingStruct, 20 + MAGIC_TLS_SERVER_ENDPOINT.length, certHashBytes.length ); final MessageDigest md5 = getMD5(); channelBindingsHash = md5.digest( channelBindingStruct ); } catch (final CertificateEncodingException | NoSuchAlgorithmException e ) { throw new NTLMEngineException( e.getMessage(), e ); } System.arraycopy( channelBindingsHash, 0, newTargetInfo, appendLength + 12, 16 ); return newTargetInfo; } } static void writeUShort(final byte[] buffer, final int value, final int offset) { buffer[offset] = ( byte ) ( value & 0xff ); buffer[offset + 1] = ( byte ) ( value >> 8 & 0xff ); } static void writeULong(final byte[] buffer, final int value, final int offset) { buffer[offset] = (byte) (value & 0xff); buffer[offset + 1] = (byte) (value >> 8 & 0xff); buffer[offset + 2] = (byte) (value >> 16 & 0xff); buffer[offset + 3] = (byte) (value >> 24 & 0xff); } static int F(final int x, final int y, final int z) { return ((x & y) | (~x & z)); } static int G(final int x, final int y, final int z) { return ((x & y) | (x & z) | (y & z)); } static int H(final int x, final int y, final int z) { return (x ^ y ^ z); } static int rotintlft(final int val, final int numbits) { return ((val << numbits) | (val >>> (32 - numbits))); } static MessageDigest getMD5() { try { return MessageDigest.getInstance("MD5"); } catch (final NoSuchAlgorithmException ex) { throw new RuntimeException("MD5 message digest doesn't seem to exist - fatal error: "+ex.getMessage(), ex); } } /** * Cryptography support - MD4. The following class was based loosely on the * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java. * Code correctness was verified by looking at MD4.java from the jcifs * library (http://jcifs.samba.org). It was massaged extensively to the * final form found here by Karl Wright (kwright@metacarta.com). */ static class MD4 { int A = 0x67452301; int B = 0xefcdab89; int C = 0x98badcfe; int D = 0x10325476; long count; final byte[] dataBuffer = new byte[64]; MD4() { } void update(final byte[] input) { // We always deal with 512 bits at a time. Correspondingly, there is // a buffer 64 bytes long that we write data into until it gets // full. int curBufferPos = (int) (count & 63L); int inputIndex = 0; while (input.length - inputIndex + curBufferPos >= dataBuffer.length) { // We have enough data to do the next step. Do a partial copy // and a transform, updating inputIndex and curBufferPos // accordingly final int transferAmt = dataBuffer.length - curBufferPos; System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); count += transferAmt; curBufferPos = 0; inputIndex += transferAmt; processBuffer(); } // If there's anything left, copy it into the buffer and leave it. // We know there's not enough left to process. if (inputIndex < input.length) { final int transferAmt = input.length - inputIndex; System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt); count += transferAmt; curBufferPos += transferAmt; } } byte[] getOutput() { // Feed pad/length data into engine. This must round out the input // to a multiple of 512 bits. final int bufferIndex = (int) (count & 63L); final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex); final byte[] postBytes = new byte[padLen + 8]; // Leading 0x80, specified amount of zero padding, then length in // bits. postBytes[0] = (byte) 0x80; // Fill out the last 8 bytes with the length for (int i = 0; i < 8; i++) { postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i)); } // Update the engine update(postBytes); // Calculate final result final byte[] result = new byte[16]; writeULong(result, A, 0); writeULong(result, B, 4); writeULong(result, C, 8); writeULong(result, D, 12); return result; } void processBuffer() { // Convert current buffer to 16 ulongs final int[] d = new int[16]; for (int i = 0; i < 16; i++) { d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8) + ((dataBuffer[i * 4 + 2] & 0xff) << 16) + ((dataBuffer[i * 4 + 3] & 0xff) << 24); } // Do a round of processing final int AA = A; final int BB = B; final int CC = C; final int DD = D; round1(d); round2(d); round3(d); A += AA; B += BB; C += CC; D += DD; } void round1(final int[] d) { A = rotintlft((A + F(B, C, D) + d[0]), 3); D = rotintlft((D + F(A, B, C) + d[1]), 7); C = rotintlft((C + F(D, A, B) + d[2]), 11); B = rotintlft((B + F(C, D, A) + d[3]), 19); A = rotintlft((A + F(B, C, D) + d[4]), 3); D = rotintlft((D + F(A, B, C) + d[5]), 7); C = rotintlft((C + F(D, A, B) + d[6]), 11); B = rotintlft((B + F(C, D, A) + d[7]), 19); A = rotintlft((A + F(B, C, D) + d[8]), 3); D = rotintlft((D + F(A, B, C) + d[9]), 7); C = rotintlft((C + F(D, A, B) + d[10]), 11); B = rotintlft((B + F(C, D, A) + d[11]), 19); A = rotintlft((A + F(B, C, D) + d[12]), 3); D = rotintlft((D + F(A, B, C) + d[13]), 7); C = rotintlft((C + F(D, A, B) + d[14]), 11); B = rotintlft((B + F(C, D, A) + d[15]), 19); } void round2(final int[] d) { A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3); D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5); C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9); B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13); A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3); D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5); C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9); B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13); A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3); D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5); C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9); B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13); A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3); D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5); C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9); B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13); } void round3(final int[] d) { A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3); D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9); C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11); B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15); A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3); D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9); C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11); B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15); A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3); D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9); C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11); B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15); A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3); D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9); C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11); B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15); } } /** * Cryptography support - HMACMD5 - algorithmically based on various web * resources by Karl Wright */ static class HMACMD5 { final byte[] ipad; final byte[] opad; final MessageDigest md5; HMACMD5(final byte[] input) { byte[] key = input; md5 = getMD5(); // Initialize the pad buffers with the key ipad = new byte[64]; opad = new byte[64]; int keyLength = key.length; if (keyLength > 64) { // Use MD5 of the key instead, as described in RFC 2104 md5.update(key); key = md5.digest(); keyLength = key.length; } int i = 0; while (i < keyLength) { ipad[i] = (byte) (key[i] ^ (byte) 0x36); opad[i] = (byte) (key[i] ^ (byte) 0x5c); i++; } while (i < 64) { ipad[i] = (byte) 0x36; opad[i] = (byte) 0x5c; i++; } // Very important: processChallenge the digest with the ipad buffer md5.reset(); md5.update(ipad); } /** Grab the current digest. This is the "answer". */ byte[] getOutput() { final byte[] digest = md5.digest(); md5.update(opad); return md5.digest(digest); } /** Update by adding a complete array */ void update(final byte[] input) { md5.update(input); } /** Update the algorithm */ void update(final byte[] input, final int offset, final int length) { md5.update(input, offset, length); } } @Override public String generateType1Msg( final String domain, final String workstation) throws NTLMEngineException { return getType1Message(workstation, domain); } @Override public String generateType3Msg( final String username, final char[] password, final String domain, final String workstation, final String challenge) throws NTLMEngineException { final Type2Message t2m = new Type2Message(challenge); return getType3Message( username, password, workstation, domain, t2m.getChallenge(), t2m.getFlags(), t2m.getTarget(), t2m.getTargetInfo()); } } NTLMScheme.java000066400000000000000000000152761434266521000352540ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.security.Principal; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.auth.NTCredentials; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * NTLM is a proprietary authentication scheme developed by Microsoft * and optimized for Windows platforms. * * @since 4.0 */ public final class NTLMScheme implements AuthScheme { private static final Logger LOG = LoggerFactory.getLogger(NTLMScheme.class); enum State { UNINITIATED, CHALLENGE_RECEIVED, MSG_TYPE1_GENERATED, MSG_TYPE2_RECEIVED, MSG_TYPE3_GENERATED, FAILED, } private final NTLMEngine engine; private State state; private String challenge; private NTCredentials credentials; public NTLMScheme(final NTLMEngine engine) { super(); Args.notNull(engine, "NTLM engine"); this.engine = engine; this.state = State.UNINITIATED; } /** * @since 4.3 */ public NTLMScheme() { this(new NTLMEngineImpl()); } @Override public String getName() { return StandardAuthScheme.NTLM; } @Override public boolean isConnectionBased() { return true; } @Override public String getRealm() { return null; } @Override public void processChallenge( final AuthChallenge authChallenge, final HttpContext context) throws MalformedChallengeException { Args.notNull(authChallenge, "AuthChallenge"); this.challenge = authChallenge.getValue(); if (this.challenge == null || this.challenge.isEmpty()) { if (this.state == State.UNINITIATED) { this.state = State.CHALLENGE_RECEIVED; } else { this.state = State.FAILED; } } else { if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) { this.state = State.FAILED; throw new MalformedChallengeException("Out of sequence NTLM response message"); } else if (this.state == State.MSG_TYPE1_GENERATED) { this.state = State.MSG_TYPE2_RECEIVED; } } } @Override public boolean isResponseReady( final HttpHost host, final CredentialsProvider credentialsProvider, final HttpContext context) throws AuthenticationException { Args.notNull(host, "Auth host"); Args.notNull(credentialsProvider, "CredentialsProvider"); final AuthScope authScope = new AuthScope(host, null, getName()); final Credentials credentials = credentialsProvider.getCredentials( authScope, context); if (credentials instanceof NTCredentials) { this.credentials = (NTCredentials) credentials; return true; } if (LOG.isDebugEnabled()) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); LOG.debug("{} No credentials found for auth scope [{}]", exchangeId, authScope); } return false; } @Override public Principal getPrincipal() { return this.credentials != null ? this.credentials.getUserPrincipal() : null; } @Override public String generateAuthResponse( final HttpHost host, final HttpRequest request, final HttpContext context) throws AuthenticationException { if (this.credentials == null) { throw new AuthenticationException("NT credentials not available"); } final String response; if (this.state == State.FAILED) { throw new AuthenticationException("NTLM authentication failed"); } else if (this.state == State.CHALLENGE_RECEIVED) { response = this.engine.generateType1Msg( this.credentials.getNetbiosDomain(), this.credentials.getWorkstation()); this.state = State.MSG_TYPE1_GENERATED; } else if (this.state == State.MSG_TYPE2_RECEIVED) { response = this.engine.generateType3Msg( this.credentials.getUserName(), this.credentials.getPassword(), this.credentials.getNetbiosDomain(), this.credentials.getWorkstation(), this.challenge); this.state = State.MSG_TYPE3_GENERATED; } else { throw new AuthenticationException("Unexpected state: " + this.state); } return StandardAuthScheme.NTLM + " " + response; } @Override public boolean isChallengeComplete() { return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED; } @Override public String toString() { return getName() + "{" + this.state + " " + challenge + '}'; } } NTLMSchemeFactory.java000066400000000000000000000040011434266521000365640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link AuthSchemeFactory} implementation that creates and initializes * {@link NTLMScheme} instances configured to use the default {@link NTLMEngine} * implementation. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public class NTLMSchemeFactory implements AuthSchemeFactory { /** * Singleton instance. */ public static final NTLMSchemeFactory INSTANCE = new NTLMSchemeFactory(); @Override public AuthScheme create(final HttpContext context) { return new NTLMScheme(); } } SPNegoScheme.java000066400000000000000000000047001434266521000356230ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.KerberosConfig; import org.apache.hc.core5.annotation.Experimental; import org.ietf.jgss.GSSException; import org.ietf.jgss.Oid; /** * SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) authentication * scheme. *

* Please note this class is considered experimental and may be discontinued or removed * in the future. *

* * @since 4.2 */ @Experimental public class SPNegoScheme extends GGSSchemeBase { private static final String SPNEGO_OID = "1.3.6.1.5.5.2"; /** * @since 5.0 */ public SPNegoScheme(final KerberosConfig config, final DnsResolver dnsResolver) { super(config, dnsResolver); } public SPNegoScheme() { super(); } @Override public String getName() { return StandardAuthScheme.SPNEGO; } @Override protected byte[] generateToken(final byte[] input, final String serviceName, final String authServer) throws GSSException { return generateGSSToken(input, new Oid(SPNEGO_OID), serviceName, authServer); } @Override public boolean isConnectionBased() { return true; } } SPNegoSchemeFactory.java000066400000000000000000000053251434266521000371570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.KerberosConfig; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link AuthSchemeFactory} implementation that creates and initializes * {@link SPNegoScheme} instances. *

* Please note this class is considered experimental and may be discontinued or removed * in the future. *

* * @since 4.2 */ @Contract(threading = ThreadingBehavior.STATELESS) @Experimental public class SPNegoSchemeFactory implements AuthSchemeFactory { /** * Singleton instance for the default configuration. */ public static final SPNegoSchemeFactory DEFAULT = new SPNegoSchemeFactory(KerberosConfig.DEFAULT, SystemDefaultDnsResolver.INSTANCE); private final KerberosConfig config; private final DnsResolver dnsResolver; /** * @since 5.0 */ public SPNegoSchemeFactory(final KerberosConfig config, final DnsResolver dnsResolver) { super(); this.config = config; this.dnsResolver = dnsResolver; } @Override public AuthScheme create(final HttpContext context) { return new SPNegoScheme(this.config, this.dnsResolver); } } SingleCredentialsProvider.java000066400000000000000000000045531434266521000404630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; final class SingleCredentialsProvider implements CredentialsProvider { private final AuthScope authScope; private final Credentials credentials; public SingleCredentialsProvider(final AuthScope authScope, final Credentials credentials) { super(); this.authScope = Args.notNull(authScope, "Auth scope"); this.credentials = credentials; } public SingleCredentialsProvider(final AuthScope authScope, final String username, final char[] password) { this(authScope, new UsernamePasswordCredentials(username, password)); } @Override public Credentials getCredentials(final AuthScope authScope, final HttpContext context) { return this.authScope.match(authScope) >= 0 ? credentials : null; } @Override public String toString() { return authScope.toString(); } } SystemDefaultCredentialsProvider.java000066400000000000000000000163541434266521000420350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.net.Authenticator; import java.net.MalformedURLException; import java.net.PasswordAuthentication; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsStore; import org.apache.hc.client5.http.auth.NTCredentials; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Implementation of {@link CredentialsStore} backed by standard * JRE {@link Authenticator}. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE) public class SystemDefaultCredentialsProvider implements CredentialsStore { private final BasicCredentialsProvider internal; /** * Default constructor. */ public SystemDefaultCredentialsProvider() { super(); this.internal = new BasicCredentialsProvider(); } @Override public void setCredentials(final AuthScope authScope, final Credentials credentials) { internal.setCredentials(authScope, credentials); } private static PasswordAuthentication getSystemCreds( final String protocol, final AuthScope authScope, final Authenticator.RequestorType requestorType, final HttpClientContext context) { final HttpRequest request = context != null ? context.getRequest() : null; URL targetHostURL; try { final URI uri = request != null ? request.getUri() : null; targetHostURL = uri != null ? uri.toURL() : null; } catch (final URISyntaxException | MalformedURLException ignore) { targetHostURL = null; } // use null addr, because the authentication fails if it does not exactly match the expected realm's host return Authenticator.requestPasswordAuthentication( authScope.getHost(), null, authScope.getPort(), protocol, authScope.getRealm(), authScope.getSchemeName(), targetHostURL, requestorType); } @Override public Credentials getCredentials(final AuthScope authScope, final HttpContext context) { Args.notNull(authScope, "Auth scope"); final Credentials localcreds = internal.getCredentials(authScope, context); if (localcreds != null) { return localcreds; } final String host = authScope.getHost(); if (host != null) { final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : null; final String protocol = authScope.getProtocol() != null ? authScope.getProtocol() : (authScope.getPort() == 443 ? URIScheme.HTTPS.id : URIScheme.HTTP.id); PasswordAuthentication systemcreds = getSystemCreds( protocol, authScope, Authenticator.RequestorType.SERVER, clientContext); if (systemcreds == null) { systemcreds = getSystemCreds( protocol, authScope, Authenticator.RequestorType.PROXY, clientContext); } if (systemcreds == null) { // Look for values given using http.proxyUser/http.proxyPassword or // https.proxyUser/https.proxyPassword. We cannot simply use the protocol from // the origin since a proxy retrieved from https.proxyHost/https.proxyPort will // still use http as protocol systemcreds = getProxyCredentials(URIScheme.HTTP.getId(), authScope); if (systemcreds == null) { systemcreds = getProxyCredentials(URIScheme.HTTPS.getId(), authScope); } } if (systemcreds != null) { final String domain = System.getProperty("http.auth.ntlm.domain"); if (domain != null) { return new NTCredentials(systemcreds.getUserName(), systemcreds.getPassword(), null, domain); } if (StandardAuthScheme.NTLM.equalsIgnoreCase(authScope.getSchemeName())) { // Domain may be specified in a fully qualified user name return new NTCredentials( systemcreds.getUserName(), systemcreds.getPassword(), null, null); } return new UsernamePasswordCredentials(systemcreds.getUserName(), systemcreds.getPassword()); } } return null; } private static PasswordAuthentication getProxyCredentials(final String protocol, final AuthScope authScope) { final String proxyHost = System.getProperty(protocol + ".proxyHost"); if (proxyHost == null) { return null; } final String proxyPort = System.getProperty(protocol + ".proxyPort"); if (proxyPort == null) { return null; } try { final AuthScope systemScope = new AuthScope(proxyHost, Integer.parseInt(proxyPort)); if (authScope.match(systemScope) >= 0) { final String proxyUser = System.getProperty(protocol + ".proxyUser"); if (proxyUser == null) { return null; } final String proxyPassword = System.getProperty(protocol + ".proxyPassword"); return new PasswordAuthentication(proxyUser, proxyPassword != null ? proxyPassword.toCharArray() : new char[] {}); } } catch (final NumberFormatException ex) { } return null; } @Override public void clear() { internal.clear(); } } UnsupportedDigestAlgorithmException.java000066400000000000000000000045531434266521000425670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; /** * Authentication credentials required to respond to a authentication * challenge are invalid * * @since 4.0 */ public class UnsupportedDigestAlgorithmException extends RuntimeException { private static final long serialVersionUID = 319558534317118022L; /** * Creates a new UnsupportedDigestAlgorithmException with a {@code null} detail message. */ public UnsupportedDigestAlgorithmException() { super(); } /** * Creates a new UnsupportedDigestAlgorithmException with the specified message. * * @param message the exception detail message */ public UnsupportedDigestAlgorithmException(final String message) { super(message); } /** * Creates a new UnsupportedDigestAlgorithmException with the specified detail message and cause. * * @param message the exception detail message * @param cause the {@code Throwable} that caused this exception, or {@code null} * if the cause is unavailable, unknown, or not a {@code Throwable} */ public UnsupportedDigestAlgorithmException(final String message, final Throwable cause) { super(message, cause); } } package-info.java000066400000000000000000000024131434266521000356660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Standard and common HTTP authentication schemes. */ package org.apache.hc.client5.http.impl.auth; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/000077500000000000000000000000001434266521000332365ustar00rootroot00000000000000AIMDBackoffManager.java000066400000000000000000000145451434266521000373140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.BackoffManager; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; /** *

The {@code AIMDBackoffManager} applies an additive increase, * multiplicative decrease (AIMD) to managing a dynamic limit to * the number of connections allowed to a given host. You may want * to experiment with the settings for the cooldown periods and the * backoff factor to get the adaptive behavior you want.

* *

Generally speaking, shorter cooldowns will lead to more steady-state * variability but faster reaction times, while longer cooldowns * will lead to more stable equilibrium behavior but slower reaction * times.

* *

Similarly, higher backoff factors promote greater * utilization of available capacity at the expense of fairness * among clients. Lower backoff factors allow equal distribution of * capacity among clients (fairness) to happen faster, at the * expense of having more server capacity unused in the short term.

* * @since 4.2 */ @Experimental public class AIMDBackoffManager implements BackoffManager { private final ConnPoolControl connPerRoute; private final Clock clock; private final Map lastRouteProbes; private final Map lastRouteBackoffs; private TimeValue coolDown = TimeValue.ofSeconds(5L); private double backoffFactor = 0.5; private int cap = 2; // Per RFC 2616 sec 8.1.4 /** * Creates an {@code AIMDBackoffManager} to manage * per-host connection pool sizes represented by the * given {@link ConnPoolControl}. * @param connPerRoute per-host routing maximums to * be managed */ public AIMDBackoffManager(final ConnPoolControl connPerRoute) { this(connPerRoute, new SystemClock()); } AIMDBackoffManager(final ConnPoolControl connPerRoute, final Clock clock) { this.clock = clock; this.connPerRoute = connPerRoute; this.lastRouteProbes = new HashMap<>(); this.lastRouteBackoffs = new HashMap<>(); } @Override public void backOff(final HttpRoute route) { synchronized(connPerRoute) { final int curr = connPerRoute.getMaxPerRoute(route); final Long lastUpdate = getLastUpdate(lastRouteBackoffs, route); final long now = clock.getCurrentTime(); if (now - lastUpdate < coolDown.toMilliseconds()) { return; } connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr)); lastRouteBackoffs.put(route, now); } } private int getBackedOffPoolSize(final int curr) { if (curr <= 1) { return 1; } return (int)(Math.floor(backoffFactor * curr)); } @Override public void probe(final HttpRoute route) { synchronized(connPerRoute) { final int curr = connPerRoute.getMaxPerRoute(route); final int max = (curr >= cap) ? cap : curr + 1; final Long lastProbe = getLastUpdate(lastRouteProbes, route); final Long lastBackoff = getLastUpdate(lastRouteBackoffs, route); final long now = clock.getCurrentTime(); if (now - lastProbe < coolDown.toMilliseconds() || now - lastBackoff < coolDown.toMilliseconds()) { return; } connPerRoute.setMaxPerRoute(route, max); lastRouteProbes.put(route, now); } } private Long getLastUpdate(final Map updates, final HttpRoute route) { Long lastUpdate = updates.get(route); if (lastUpdate == null) { lastUpdate = 0L; } return lastUpdate; } /** * Sets the factor to use when backing off; the new * per-host limit will be roughly the current max times * this factor. {@code Math.floor} is applied in the * case of non-integer outcomes to ensure we actually * decrease the pool size. Pool sizes are never decreased * below 1, however. Defaults to 0.5. * @param d must be between 0.0 and 1.0, exclusive. */ public void setBackoffFactor(final double d) { Args.check(d > 0.0 && d < 1.0, "Backoff factor must be 0.0 < f < 1.0"); backoffFactor = d; } /** * Sets the amount of time to wait between adjustments in * pool sizes for a given host, to allow enough time for * the adjustments to take effect. Defaults to 5 seconds. * @param coolDown must be positive */ public void setCoolDown(final TimeValue coolDown) { Args.positive(coolDown.getDuration(), "coolDown"); this.coolDown = coolDown; } /** * Sets the absolute maximum per-host connection pool size to * probe up to; defaults to 2 (the default per-host max). * @param cap must be >= 1 */ public void setPerHostConnectionCap(final int cap) { Args.positive(cap, "Per host connection cap"); this.cap = cap; } } AbstractHttpClientResponseHandler.java000066400000000000000000000063161434266521000426070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.HttpResponseException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; /** * A generic {@link HttpClientResponseHandler} that works with the response entity * for successful (2xx) responses. If the response code was >= 300, the response * body is consumed and an {@link HttpResponseException} is thrown. *

* If this is used with * {@link org.apache.hc.client5.http.classic.HttpClient#execute( * org.apache.hc.core5.http.ClassicHttpRequest, HttpClientResponseHandler)}, * HttpClient may handle redirects (3xx responses) internally. *

* * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public abstract class AbstractHttpClientResponseHandler implements HttpClientResponseHandler { /** * Read the entity from the response body and pass it to the entity handler * method if the response was successful (a 2xx status code). If no response * body exists, this returns null. If the response was unsuccessful (>= 300 * status code), throws an {@link HttpResponseException}. */ @Override public T handleResponse(final ClassicHttpResponse response) throws IOException { final HttpEntity entity = response.getEntity(); if (response.getCode() >= HttpStatus.SC_REDIRECTION) { EntityUtils.consume(entity); throw new HttpResponseException(response.getCode(), response.getReasonPhrase()); } return entity == null ? null : handleEntity(entity); } /** * Handle the response entity and transform it into the actual response * object. */ public abstract T handleEntity(HttpEntity entity) throws IOException; } BackoffStrategyExec.java000066400000000000000000000073541434266521000377160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.BackoffManager; import org.apache.hc.client5.http.classic.ConnectionBackoffStrategy; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.util.Args; /** * Request execution handler in the classic request execution chain * that is responsible for execution of an {@link ConnectionBackoffStrategy}. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) @Experimental public final class BackoffStrategyExec implements ExecChainHandler { private final ConnectionBackoffStrategy connectionBackoffStrategy; private final BackoffManager backoffManager; public BackoffStrategyExec( final ConnectionBackoffStrategy connectionBackoffStrategy, final BackoffManager backoffManager) { super(); Args.notNull(connectionBackoffStrategy, "Connection backoff strategy"); Args.notNull(backoffManager, "Backoff manager"); this.connectionBackoffStrategy = connectionBackoffStrategy; this.backoffManager = backoffManager; } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final HttpRoute route = scope.route; final ClassicHttpResponse response; try { response = chain.proceed(request, scope); } catch (final IOException | HttpException ex) { if (this.connectionBackoffStrategy.shouldBackoff(ex)) { this.backoffManager.backOff(route); } throw ex; } if (this.connectionBackoffStrategy.shouldBackoff(response)) { this.backoffManager.backOff(route); } else { this.backoffManager.probe(route); } return response; } } BasicHttpClientResponseHandler.java000066400000000000000000000054561434266521000420710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.io.entity.EntityUtils; /** * A {@link org.apache.hc.core5.http.io.HttpClientResponseHandler} that returns * the response body as a String for successful (2xx) responses. If the response * code was >= 300, the response body is consumed * and an {@link org.apache.hc.client5.http.HttpResponseException} is thrown. *

* If this is used with * {@link org.apache.hc.client5.http.classic.HttpClient#execute( * org.apache.hc.core5.http.ClassicHttpRequest, * org.apache.hc.core5.http.io.HttpClientResponseHandler)}, * HttpClient may handle redirects (3xx responses) internally. *

* * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicHttpClientResponseHandler extends AbstractHttpClientResponseHandler { /** * Returns the entity as a body as a String. */ @Override public String handleEntity(final HttpEntity entity) throws IOException { try { return EntityUtils.toString(entity); } catch (final ParseException ex) { throw new ClientProtocolException(ex); } } @Override public String handleResponse(final ClassicHttpResponse response) throws IOException { return super.handleResponse(response); } } ClassicRequestCopier.java000066400000000000000000000045641434266521000401270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.util.Iterator; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; /** * {@link ClassicHttpRequest} copier. * * @since 5.0 * * @deprecated Use {@link org.apache.hc.core5.http.io.support.ClassicRequestBuilder} */ @Deprecated public final class ClassicRequestCopier implements org.apache.hc.client5.http.impl.MessageCopier { public static final ClassicRequestCopier INSTANCE = new ClassicRequestCopier(); @Override public ClassicHttpRequest copy(final ClassicHttpRequest original) { if (original == null) { return null; } final BasicClassicHttpRequest copy = new BasicClassicHttpRequest(original.getMethod(), null, original.getPath()); copy.setScheme(original.getScheme()); copy.setAuthority(original.getAuthority()); copy.setVersion(original.getVersion()); for (final Iterator
it = original.headerIterator(); it.hasNext(); ) { copy.addHeader(it.next()); } copy.setEntity(original.getEntity()); return copy; } } Clock.java000066400000000000000000000026171434266521000350630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; interface Clock { /** * Returns the current time, expressed as the number of * milliseconds since the epoch. * @return current time */ long getCurrentTime(); } CloseableHttpClient.java000066400000000000000000000302351434266521000377150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base implementation of {@link HttpClient} that also implements {@link ModalCloseable}. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class CloseableHttpClient implements HttpClient, ModalCloseable { private static final Logger LOG = LoggerFactory.getLogger(CloseableHttpClient.class); protected abstract CloseableHttpResponse doExecute(HttpHost target, ClassicHttpRequest request, HttpContext context) throws IOException; private static HttpHost determineTarget(final ClassicHttpRequest request) throws ClientProtocolException { try { return RoutingSupport.determineHost(request); } catch (final HttpException ex) { throw new ClientProtocolException(ex); } } /** * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(HttpHost, ClassicHttpRequest, HttpContext, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(HttpHost, ClassicHttpRequest, HttpContext, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated @Override public CloseableHttpResponse execute( final HttpHost target, final ClassicHttpRequest request, final HttpContext context) throws IOException { return doExecute(target, request, context); } /** * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(ClassicHttpRequest, HttpContext, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(ClassicHttpRequest, HttpContext, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated @Override public CloseableHttpResponse execute( final ClassicHttpRequest request, final HttpContext context) throws IOException { Args.notNull(request, "HTTP request"); return doExecute(determineTarget(request), request, context); } /** * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(ClassicHttpRequest, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(ClassicHttpRequest, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated @Override public CloseableHttpResponse execute( final ClassicHttpRequest request) throws IOException { return doExecute(determineTarget(request), request, null); } /** * @deprecated It is strongly recommended to use execute methods with {@link HttpClientResponseHandler} * such as {@link #execute(HttpHost, ClassicHttpRequest, HttpClientResponseHandler)} in order * to ensure automatic resource deallocation by the client. * For special cases one can still use {@link #executeOpen(HttpHost, ClassicHttpRequest, HttpContext)} * to keep the response object open after the request execution. * * @see #execute(HttpHost, ClassicHttpRequest, HttpClientResponseHandler) * @see #executeOpen(HttpHost, ClassicHttpRequest, HttpContext) */ @Deprecated @Override public CloseableHttpResponse execute( final HttpHost target, final ClassicHttpRequest request) throws IOException { return doExecute(target, request, null); } /** * Executes a request using the default context and processes the * response using the given response handler. The content entity associated * with the response is fully consumed and the underlying connection is * released back to the connection manager automatically in all cases * relieving individual {@link HttpClientResponseHandler}s from having to manage * resource deallocation internally. * * @param request the request to execute * @param responseHandler the response handler * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted * @throws ClientProtocolException in case of an http protocol error */ @Override public T execute(final ClassicHttpRequest request, final HttpClientResponseHandler responseHandler) throws IOException { return execute(request, null, responseHandler); } /** * Executes a request using the default context and processes the * response using the given response handler. The content entity associated * with the response is fully consumed and the underlying connection is * released back to the connection manager automatically in all cases * relieving individual {@link HttpClientResponseHandler}s from having to manage * resource deallocation internally. * * @param request the request to execute * @param responseHandler the response handler * @param context the context to use for the execution, or * {@code null} to use the default context * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted * @throws ClientProtocolException in case of an http protocol error */ @Override public T execute( final ClassicHttpRequest request, final HttpContext context, final HttpClientResponseHandler responseHandler) throws IOException { final HttpHost target = determineTarget(request); return execute(target, request, context, responseHandler); } /** * Executes a request using the default context and processes the * response using the given response handler. The content entity associated * with the response is fully consumed and the underlying connection is * released back to the connection manager automatically in all cases * relieving individual {@link HttpClientResponseHandler}s from having to manage * resource deallocation internally. * * @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 responseHandler the response handler * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted * @throws ClientProtocolException in case of an http protocol error */ @Override public T execute(final HttpHost target, final ClassicHttpRequest request, final HttpClientResponseHandler responseHandler) throws IOException { return execute(target, request, null, responseHandler); } /** * Executes a request using the default context and processes the * response using the given response handler. The content entity associated * with the response is fully consumed and the underlying connection is * released back to the connection manager automatically in all cases * relieving individual {@link HttpClientResponseHandler}s from having to manage * resource deallocation internally. * * @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 responseHandler the response handler * * @return the response object as generated by the response handler. * @throws IOException in case of a problem or the connection was aborted * @throws ClientProtocolException in case of an http protocol error */ @Override public T execute( final HttpHost target, final ClassicHttpRequest request, final HttpContext context, final HttpClientResponseHandler responseHandler) throws IOException { Args.notNull(responseHandler, "Response handler"); try (final ClassicHttpResponse response = doExecute(target, request, context)) { try { final T result = responseHandler.handleResponse(response); final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); return result; } catch (final HttpException t) { // Try to salvage the underlying connection in case of a protocol exception final HttpEntity entity = response.getEntity(); try { EntityUtils.consume(entity); } catch (final Exception t2) { // Log this exception. The original exception is more // important and will be thrown to the caller. LOG.warn("Error consuming content after an exception.", t2); } throw new ClientProtocolException(t); } } } } CloseableHttpResponse.java000066400000000000000000000132131434266521000402720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.util.Iterator; import java.util.Locale; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.util.Args; /** * Provided for backward compatibility with HttpClient 4.x. * * @since 4.3 */ public final class CloseableHttpResponse implements ClassicHttpResponse { private final ClassicHttpResponse response; private final ExecRuntime execRuntime; static CloseableHttpResponse adapt(final ClassicHttpResponse response) { if (response == null) { return null; } return response instanceof CloseableHttpResponse ? (CloseableHttpResponse) response : new CloseableHttpResponse(response, null); } CloseableHttpResponse(final ClassicHttpResponse response, final ExecRuntime execRuntime) { this.response = Args.notNull(response, "Response"); this.execRuntime = execRuntime; } @Override public int getCode() { return response.getCode(); } @Override public HttpEntity getEntity() { return response.getEntity(); } @Override public boolean containsHeader(final String name) { return response.containsHeader(name); } @Override public void setVersion(final ProtocolVersion version) { response.setVersion(version); } @Override public void setCode(final int code) { response.setCode(code); } @Override public String getReasonPhrase() { return response.getReasonPhrase(); } @Override public int countHeaders(final String name) { return response.countHeaders(name); } @Override public void setEntity(final HttpEntity entity) { response.setEntity(entity); } @Override public ProtocolVersion getVersion() { return response.getVersion(); } @Override public void setReasonPhrase(final String reason) { response.setReasonPhrase(reason); } @Override public Header[] getHeaders(final String name) { return response.getHeaders(name); } @Override public void addHeader(final Header header) { response.addHeader(header); } @Override public Locale getLocale() { return response.getLocale(); } @Override public void addHeader(final String name, final Object value) { response.addHeader(name, value); } @Override public void setLocale(final Locale loc) { response.setLocale(loc); } @Override public Header getHeader(final String name) throws ProtocolException { return response.getHeader(name); } @Override public void setHeader(final Header header) { response.setHeader(header); } @Override public Header getFirstHeader(final String name) { return response.getFirstHeader(name); } @Override public void setHeader(final String name, final Object value) { response.setHeader(name, value); } @Override public void setHeaders(final Header... headers) { response.setHeaders(headers); } @Override public boolean removeHeader(final Header header) { return response.removeHeader(header); } @Override public boolean removeHeaders(final String name) { return response.removeHeaders(name); } @Override public Header getLastHeader(final String name) { return response.getLastHeader(name); } @Override public Header[] getHeaders() { return response.getHeaders(); } @Override public Iterator
headerIterator() { return response.headerIterator(); } @Override public Iterator
headerIterator(final String name) { return response.headerIterator(name); } @Override public void close() throws IOException { if (execRuntime != null) { try { response.close(); execRuntime.disconnectEndpoint(); } finally { execRuntime.discardEndpoint(); } } else { response.close(); } } @Override public String toString() { return response.toString(); } } ConnectExec.java000066400000000000000000000343631434266521000362310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteTracker; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.TunnelRefusedException; import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper; import org.apache.hc.client5.http.impl.auth.HttpAuthenticator; import org.apache.hc.client5.http.impl.routing.BasicRouteDirector; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRouteDirector; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request execution handler in the classic request execution chain * that is responsible for establishing connection to the target * origin server as specified by the current connection route. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class ConnectExec implements ExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(ConnectExec.class); private final ConnectionReuseStrategy reuseStrategy; private final HttpProcessor proxyHttpProcessor; private final AuthenticationStrategy proxyAuthStrategy; private final HttpAuthenticator authenticator; private final AuthCacheKeeper authCacheKeeper; private final HttpRouteDirector routeDirector; public ConnectExec( final ConnectionReuseStrategy reuseStrategy, final HttpProcessor proxyHttpProcessor, final AuthenticationStrategy proxyAuthStrategy, final SchemePortResolver schemePortResolver, final boolean authCachingDisabled) { Args.notNull(reuseStrategy, "Connection reuse strategy"); Args.notNull(proxyHttpProcessor, "Proxy HTTP processor"); Args.notNull(proxyAuthStrategy, "Proxy authentication strategy"); this.reuseStrategy = reuseStrategy; this.proxyHttpProcessor = proxyHttpProcessor; this.proxyAuthStrategy = proxyAuthStrategy; this.authenticator = new HttpAuthenticator(); this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(schemePortResolver); this.routeDirector = BasicRouteDirector.INSTANCE; } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final HttpClientContext context = scope.clientContext; final ExecRuntime execRuntime = scope.execRuntime; if (!execRuntime.isEndpointAcquired()) { final Object userToken = context.getUserToken(); if (LOG.isDebugEnabled()) { LOG.debug("{} acquiring connection with route {}", exchangeId, route); } execRuntime.acquireEndpoint(exchangeId, route, userToken, context); } try { if (!execRuntime.isEndpointConnected()) { if (LOG.isDebugEnabled()) { LOG.debug("{} opening connection {}", exchangeId, route); } final RouteTracker tracker = new RouteTracker(route); int step; do { final HttpRoute fact = tracker.toRoute(); step = this.routeDirector.nextStep(route, fact); switch (step) { case HttpRouteDirector.CONNECT_TARGET: execRuntime.connectEndpoint(context); tracker.connectTarget(route.isSecure()); break; case HttpRouteDirector.CONNECT_PROXY: execRuntime.connectEndpoint(context); final HttpHost proxy = route.getProxyHost(); tracker.connectProxy(proxy, route.isSecure() && !route.isTunnelled()); break; case HttpRouteDirector.TUNNEL_TARGET: { final boolean secure = createTunnelToTarget(exchangeId, route, request, execRuntime, context); if (LOG.isDebugEnabled()) { LOG.debug("{} tunnel to target created.", exchangeId); } tracker.tunnelTarget(secure); } break; case HttpRouteDirector.TUNNEL_PROXY: { // The most simple example for this case is a proxy chain // of two proxies, where P1 must be tunnelled to P2. // route: Source -> P1 -> P2 -> Target (3 hops) // fact: Source -> P1 -> Target (2 hops) final int hop = fact.getHopCount()-1; // the hop to establish final boolean secure = createTunnelToProxy(route, hop, context); if (LOG.isDebugEnabled()) { LOG.debug("{} tunnel to proxy created.", exchangeId); } tracker.tunnelProxy(route.getHopTarget(hop), secure); } break; case HttpRouteDirector.LAYER_PROTOCOL: execRuntime.upgradeTls(context); tracker.layerProtocol(route.isSecure()); break; case HttpRouteDirector.UNREACHABLE: throw new HttpException("Unable to establish route: " + "planned = " + route + "; current = " + fact); case HttpRouteDirector.COMPLETE: break; default: throw new IllegalStateException("Unknown step indicator " + step + " from RouteDirector."); } } while (step > HttpRouteDirector.COMPLETE); } return chain.proceed(request, scope); } catch (final IOException | HttpException | RuntimeException ex) { execRuntime.discardEndpoint(); throw ex; } } /** * Creates a tunnel to the target server. * The connection must be established to the (last) proxy. * A CONNECT request for tunnelling through the proxy will * be created and sent, the response received and checked. * This method does not processChallenge the connection with * information about the tunnel, that is left to the caller. */ private boolean createTunnelToTarget( final String exchangeId, final HttpRoute route, final HttpRequest request, final ExecRuntime execRuntime, final HttpClientContext context) throws HttpException, IOException { final RequestConfig config = context.getRequestConfig(); final HttpHost target = route.getTargetHost(); final HttpHost proxy = route.getProxyHost(); final AuthExchange proxyAuthExchange = context.getAuthExchange(proxy); if (authCacheKeeper != null) { authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, context); } ClassicHttpResponse response = null; final String authority = target.toHostString(); final ClassicHttpRequest connect = new BasicClassicHttpRequest(Method.CONNECT, target, authority); connect.setVersion(HttpVersion.HTTP_1_1); this.proxyHttpProcessor.process(connect, null, context); while (response == null) { connect.removeHeaders(HttpHeaders.PROXY_AUTHORIZATION); this.authenticator.addAuthResponse(proxy, ChallengeType.PROXY, connect, proxyAuthExchange, context); response = execRuntime.execute(exchangeId, connect, context); this.proxyHttpProcessor.process(response, response.getEntity(), context); final int status = response.getCode(); if (status < HttpStatus.SC_SUCCESS) { throw new HttpException("Unexpected response to CONNECT request: " + new StatusLine(response)); } if (config.isAuthenticationEnabled()) { final boolean proxyAuthRequested = authenticator.isChallenged(proxy, ChallengeType.PROXY, response, proxyAuthExchange, context); if (authCacheKeeper != null) { if (proxyAuthRequested) { authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context); } else { authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context); } } if (proxyAuthRequested) { final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response, proxyAuthStrategy, proxyAuthExchange, context); if (authCacheKeeper != null) { authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context); } if (updated) { // Retry request if (this.reuseStrategy.keepAlive(connect, response, context)) { if (LOG.isDebugEnabled()) { LOG.debug("{} connection kept alive", exchangeId); } // Consume response content final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } else { execRuntime.disconnectEndpoint(); } response = null; } } } } final int status = response.getCode(); if (status != HttpStatus.SC_OK) { // Buffer response content final HttpEntity entity = response.getEntity(); final String responseMessage = entity != null ? EntityUtils.toString(entity) : null; execRuntime.disconnectEndpoint(); throw new TunnelRefusedException("CONNECT refused by proxy: " + new StatusLine(response), responseMessage); } // How to decide on security of the tunnelled connection? // The socket factory knows only about the segment to the proxy. // Even if that is secure, the hop to the target may be insecure. // Leave it to derived classes, consider insecure by default here. return false; } /** * Creates a tunnel to an intermediate proxy. * This method is not implemented in this class. * It just throws an exception here. */ private boolean createTunnelToProxy( final HttpRoute route, final int hop, final HttpClientContext context) throws HttpException { // Have a look at createTunnelToTarget and replicate the parts // you need in a custom derived class. If your proxies don't require // authentication, it is not too hard. But for the stock version of // HttpClient, we cannot make such simplifying assumptions and would // have to include proxy authentication code. The HttpComponents team // is currently not in a position to support rarely used code of this // complexity. Feel free to submit patches that refactor the code in // createTunnelToTarget to facilitate re-use for proxy tunnelling. throw new HttpException("Proxy chains are not supported."); } } ContentCompressionExec.java000066400000000000000000000172271434266521000404740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.util.List; import java.util.Locale; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.BrotliDecompressingEntity; import org.apache.hc.client5.http.entity.BrotliInputStreamFactory; import org.apache.hc.client5.http.entity.DecompressingEntity; import org.apache.hc.client5.http.entity.DeflateInputStreamFactory; import org.apache.hc.client5.http.entity.GZIPInputStreamFactory; import org.apache.hc.client5.http.entity.InputStreamFactory; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.message.BasicHeaderValueParser; import org.apache.hc.core5.http.message.MessageSupport; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.util.Args; /** * Request execution handler in the classic request execution chain * that is responsible for automatic response content decompression. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class ContentCompressionExec implements ExecChainHandler { private final Header acceptEncoding; private final Lookup decoderRegistry; private final boolean ignoreUnknown; /** * An empty immutable {@code String} array. */ private static final String[] EMPTY_STRING_ARRAY = {}; public ContentCompressionExec( final List acceptEncoding, final Lookup decoderRegistry, final boolean ignoreUnknown) { final boolean brotliSupported = BrotliDecompressingEntity.isAvailable(); final String[] encoding; if (brotliSupported) { encoding = new String[] {"gzip", "x-gzip", "deflate", "br"}; } else { encoding = new String[] {"gzip", "x-gzip", "deflate"}; } this.acceptEncoding = MessageSupport.format(HttpHeaders.ACCEPT_ENCODING, acceptEncoding != null ? acceptEncoding.toArray( EMPTY_STRING_ARRAY) : encoding); if (decoderRegistry != null) { this.decoderRegistry = decoderRegistry; } else { final RegistryBuilder builder = RegistryBuilder.create() .register("gzip", GZIPInputStreamFactory.getInstance()) .register("x-gzip", GZIPInputStreamFactory.getInstance()) .register("deflate", DeflateInputStreamFactory.getInstance()); if (brotliSupported) { builder.register("br", BrotliInputStreamFactory.getInstance()); } this.decoderRegistry = builder.build(); } this.ignoreUnknown = ignoreUnknown; } public ContentCompressionExec(final boolean ignoreUnknown) { this(null, null, ignoreUnknown); } /** * Handles {@code gzip} and {@code deflate} compressed entities by using the following * decoders: *
    *
  • gzip - see {@link java.util.zip.GZIPInputStream}
  • *
  • deflate - see {@link org.apache.hc.client5.http.entity.DeflateInputStream}
  • *
  • brotli - see {@link org.brotli.dec.BrotliInputStream}
  • *
*/ public ContentCompressionExec() { this(null, null, true); } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final HttpClientContext clientContext = scope.clientContext; final RequestConfig requestConfig = clientContext.getRequestConfig(); /* Signal support for Accept-Encoding transfer encodings. */ if (!request.containsHeader(HttpHeaders.ACCEPT_ENCODING) && requestConfig.isContentCompressionEnabled()) { request.addHeader(acceptEncoding); } final ClassicHttpResponse response = chain.proceed(request, scope); final HttpEntity entity = response.getEntity(); // entity can be null in case of 304 Not Modified, 204 No Content or similar // check for zero length entity. if (requestConfig.isContentCompressionEnabled() && entity != null && entity.getContentLength() != 0) { final String contentEncoding = entity.getContentEncoding(); if (contentEncoding != null) { final ParserCursor cursor = new ParserCursor(0, contentEncoding.length()); final HeaderElement[] codecs = BasicHeaderValueParser.INSTANCE.parseElements(contentEncoding, cursor); for (final HeaderElement codec : codecs) { final String codecname = codec.getName().toLowerCase(Locale.ROOT); final InputStreamFactory decoderFactory = decoderRegistry.lookup(codecname); if (decoderFactory != null) { response.setEntity(new DecompressingEntity(response.getEntity(), decoderFactory)); response.removeHeaders(HttpHeaders.CONTENT_LENGTH); response.removeHeaders(HttpHeaders.CONTENT_ENCODING); response.removeHeaders(HttpHeaders.CONTENT_MD5); } else { if (!"identity".equals(codecname) && !ignoreUnknown) { throw new HttpException("Unsupported Content-Encoding: " + codec.getName()); } } } } } return response; } } DefaultBackoffStrategy.java000066400000000000000000000042751434266521000404150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.net.ConnectException; import java.net.SocketTimeoutException; import org.apache.hc.client5.http.classic.ConnectionBackoffStrategy; import org.apache.hc.core5.annotation.Experimental; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; /** * This {@link ConnectionBackoffStrategy} backs off either for a raw * network socket or connection timeout or if the server explicitly * sends a 429 (Too Many Requests) or a 503 (Service Unavailable) response. * * @since 4.2 */ @Experimental public class DefaultBackoffStrategy implements ConnectionBackoffStrategy { @Override public boolean shouldBackoff(final Throwable t) { return t instanceof SocketTimeoutException || t instanceof ConnectException; } @Override public boolean shouldBackoff(final HttpResponse response) { return response.getCode() == HttpStatus.SC_TOO_MANY_REQUESTS || response.getCode() == HttpStatus.SC_SERVICE_UNAVAILABLE; } } ExecChainElement.java000066400000000000000000000043361434266521000371710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; class ExecChainElement { private final ExecChainHandler handler; private final ExecChainElement next; ExecChainElement(final ExecChainHandler handler, final ExecChainElement next) { this.handler = handler; this.next = next; } public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope) throws IOException, HttpException { return handler.execute(request, scope, next != null ? next::execute : null); } @Override public String toString() { return "{" + "handler=" + handler.getClass() + ", next=" + (next != null ? next.handler.getClass() : "null") + '}'; } }FutureRequestExecutionMetrics.java000066400000000000000000000115341434266521000420640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.util.concurrent.atomic.AtomicLong; /** * Collection of different counters used to gather metrics for {@link FutureRequestExecutionService}. */ public final class FutureRequestExecutionMetrics { private final AtomicLong activeConnections = new AtomicLong(); private final AtomicLong scheduledConnections = new AtomicLong(); private final DurationCounter successfulConnections = new DurationCounter(); private final DurationCounter failedConnections = new DurationCounter(); private final DurationCounter requests = new DurationCounter(); private final DurationCounter tasks = new DurationCounter(); FutureRequestExecutionMetrics() { } AtomicLong getActiveConnections() { return activeConnections; } AtomicLong getScheduledConnections() { return scheduledConnections; } DurationCounter getSuccessfulConnections() { return successfulConnections; } DurationCounter getFailedConnections() { return failedConnections; } DurationCounter getRequests() { return requests; } DurationCounter getTasks() { return tasks; } public long getActiveConnectionCount() { return activeConnections.get(); } public long getScheduledConnectionCount() { return scheduledConnections.get(); } public long getSuccessfulConnectionCount() { return successfulConnections.count(); } public long getSuccessfulConnectionAverageDuration() { return successfulConnections.averageDuration(); } public long getFailedConnectionCount() { return failedConnections.count(); } public long getFailedConnectionAverageDuration() { return failedConnections.averageDuration(); } public long getRequestCount() { return requests.count(); } public long getRequestAverageDuration() { return requests.averageDuration(); } public long getTaskCount() { return tasks.count(); } public long getTaskAverageDuration() { return tasks.averageDuration(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[activeConnections=").append(activeConnections) .append(", scheduledConnections=").append(scheduledConnections) .append(", successfulConnections=").append(successfulConnections) .append(", failedConnections=").append(failedConnections) .append(", requests=").append(requests) .append(", tasks=").append(tasks) .append("]"); return builder.toString(); } /** * A counter that can measure duration and number of events. */ static class DurationCounter { private final AtomicLong count = new AtomicLong(0); private final AtomicLong cumulativeDuration = new AtomicLong(0); public void increment(final long startTime) { count.incrementAndGet(); cumulativeDuration.addAndGet(System.currentTimeMillis() - startTime); } public long count() { return count.get(); } public long averageDuration() { final long counter = count.get(); return counter > 0 ? cumulativeDuration.get() / counter : 0; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("[count=").append(count()) .append(", averageDuration=").append(averageDuration()) .append("]"); return builder.toString(); } } }FutureRequestExecutionService.java000066400000000000000000000131041434266521000420510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.http.protocol.HttpContext; /** * This class schedules message execution execution and processing * as {@link FutureTask}s with the provided {@link ExecutorService}. */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class FutureRequestExecutionService implements Closeable { private final HttpClient httpclient; private final ExecutorService executorService; private final FutureRequestExecutionMetrics metrics = new FutureRequestExecutionMetrics(); private final AtomicBoolean closed = new AtomicBoolean(false); /** * Create a new FutureRequestExecutionService. * * @param httpclient * you should tune your httpclient instance to match your needs. You should * align the max number of connections in the pool and the number of threads * in the executor; it doesn't make sense to have more threads than connections * and if you have less connections than threads, the threads will just end up * blocking on getting a connection from the pool. * @param executorService * any executorService will do here. E.g. * {@link java.util.concurrent.Executors#newFixedThreadPool(int)} */ public FutureRequestExecutionService( final HttpClient httpclient, final ExecutorService executorService) { this.httpclient = httpclient; this.executorService = executorService; } /** * Schedule a request for execution. * * @param * * @param request * request to execute * @param HttpClientResponseHandler * handler that will process the response. * @return HttpAsyncClientFutureTask for the scheduled request. */ public FutureTask execute( final ClassicHttpRequest request, final HttpContext context, final HttpClientResponseHandler HttpClientResponseHandler) { return execute(request, context, HttpClientResponseHandler, null); } /** * Schedule a request for execution. * * @param * * @param request * request to execute * @param context * optional context; use null if not needed. * @param HttpClientResponseHandler * handler that will process the response. * @param callback * callback handler that will be called when the request is scheduled, * started, completed, failed, or cancelled. * @return HttpAsyncClientFutureTask for the scheduled request. */ public FutureTask execute( final ClassicHttpRequest request, final HttpContext context, final HttpClientResponseHandler HttpClientResponseHandler, final FutureCallback callback) { if(closed.get()) { throw new IllegalStateException("Close has been called on this httpclient instance."); } metrics.getScheduledConnections().incrementAndGet(); final HttpRequestTaskCallable callable = new HttpRequestTaskCallable<>( httpclient, request, context, HttpClientResponseHandler, callback, metrics); final HttpRequestFutureTask httpRequestFutureTask = new HttpRequestFutureTask<>( request, callable); executorService.execute(httpRequestFutureTask); return httpRequestFutureTask; } /** * @return metrics gathered for this instance. * @see FutureRequestExecutionMetrics */ public FutureRequestExecutionMetrics metrics() { return metrics; } @Override public void close() throws IOException { closed.set(true); executorService.shutdownNow(); if (httpclient instanceof Closeable) { ((Closeable) httpclient).close(); } } } HttpClientBuilder.java000066400000000000000000001152521434266521000374150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.Closeable; import java.net.ProxySelector; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.BackoffManager; import org.apache.hc.client5.http.classic.ConnectionBackoffStrategy; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.entity.InputStreamFactory; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.CookieSpecSupport; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy; import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.DefaultUserTokenHandler; import org.apache.hc.client5.http.impl.IdleConnectionEvictor; import org.apache.hc.client5.http.impl.NoopUserTokenHandler; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory; import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory; import org.apache.hc.client5.http.impl.auth.KerberosSchemeFactory; import org.apache.hc.client5.http.impl.auth.NTLMSchemeFactory; import org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory; import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner; import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner; import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.RedirectStrategy; import org.apache.hc.client5.http.protocol.RequestAddCookies; import org.apache.hc.client5.http.protocol.RequestClientConnControl; import org.apache.hc.client5.http.protocol.RequestDefaultHeaders; import org.apache.hc.client5.http.protocol.RequestExpectContinue; import org.apache.hc.client5.http.protocol.ResponseProcessCookies; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.NamedElementChain; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.HttpProcessorBuilder; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.apache.hc.core5.util.VersionInfo; /** * Builder for {@link CloseableHttpClient} instances. *

* When a particular component is not explicitly set 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()}. *

*
    *
  • http.proxyHost
  • *
  • http.proxyPort
  • *
  • https.proxyHost
  • *
  • https.proxyPort
  • *
  • http.nonProxyHosts
  • *
  • https.proxyUser
  • *
  • http.proxyUser
  • *
  • https.proxyPassword
  • *
  • http.proxyPassword
  • *
  • http.keepAlive
  • *
  • http.agent
  • *
*

* Please note that some settings used by this class can be mutually * exclusive and may not apply when building {@link CloseableHttpClient} * instances. *

* * @since 4.3 */ public class HttpClientBuilder { private static class RequestInterceptorEntry { enum Position { FIRST, LAST } final RequestInterceptorEntry.Position position; final HttpRequestInterceptor interceptor; private RequestInterceptorEntry(final RequestInterceptorEntry.Position position, final HttpRequestInterceptor interceptor) { this.position = position; this.interceptor = interceptor; } } private static class ResponseInterceptorEntry { enum Position { FIRST, LAST } final ResponseInterceptorEntry.Position position; final HttpResponseInterceptor interceptor; private ResponseInterceptorEntry(final ResponseInterceptorEntry.Position position, final HttpResponseInterceptor interceptor) { this.position = position; this.interceptor = interceptor; } } private static class ExecInterceptorEntry { enum Position { BEFORE, AFTER, REPLACE, FIRST, LAST } final ExecInterceptorEntry.Position position; final String name; final ExecChainHandler interceptor; final String existing; private ExecInterceptorEntry( final ExecInterceptorEntry.Position position, final String name, final ExecChainHandler interceptor, final String existing) { this.position = position; this.name = name; this.interceptor = interceptor; this.existing = existing; } } private HttpRequestExecutor requestExec; private HttpClientConnectionManager connManager; private boolean connManagerShared; private SchemePortResolver schemePortResolver; private ConnectionReuseStrategy reuseStrategy; private ConnectionKeepAliveStrategy keepAliveStrategy; private AuthenticationStrategy targetAuthStrategy; private AuthenticationStrategy proxyAuthStrategy; private UserTokenHandler userTokenHandler; private LinkedList requestInterceptors; private LinkedList responseInterceptors; private LinkedList execInterceptors; private HttpRequestRetryStrategy retryStrategy; private HttpRoutePlanner routePlanner; private RedirectStrategy redirectStrategy; private ConnectionBackoffStrategy connectionBackoffStrategy; private BackoffManager backoffManager; private Lookup authSchemeRegistry; private Lookup cookieSpecRegistry; private LinkedHashMap contentDecoderMap; private CookieStore cookieStore; private CredentialsProvider credentialsProvider; private String userAgent; private HttpHost proxy; private Collection defaultHeaders; private RequestConfig defaultRequestConfig; private boolean evictExpiredConnections; private boolean evictIdleConnections; private TimeValue maxIdleTime; private boolean systemProperties; private boolean redirectHandlingDisabled; private boolean automaticRetriesDisabled; private boolean contentCompressionDisabled; private boolean cookieManagementDisabled; private boolean authCachingDisabled; private boolean connectionStateDisabled; private boolean defaultUserAgentDisabled; private List closeables; public static HttpClientBuilder create() { return new HttpClientBuilder(); } protected HttpClientBuilder() { super(); } /** * Assigns {@link HttpRequestExecutor} instance. */ public final HttpClientBuilder setRequestExecutor(final HttpRequestExecutor requestExec) { this.requestExec = requestExec; return this; } /** * Assigns {@link HttpClientConnectionManager} instance. */ public final HttpClientBuilder setConnectionManager( final HttpClientConnectionManager connManager) { this.connManager = connManager; return this; } /** * Defines the connection manager is to be shared by multiple * client instances. *

* 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.4 */ public final HttpClientBuilder setConnectionManagerShared( final boolean shared) { this.connManagerShared = shared; return this; } /** * Assigns {@link ConnectionReuseStrategy} instance. */ public final HttpClientBuilder setConnectionReuseStrategy( final ConnectionReuseStrategy reuseStrategy) { this.reuseStrategy = reuseStrategy; return this; } /** * Assigns {@link ConnectionKeepAliveStrategy} instance. */ public final HttpClientBuilder setKeepAliveStrategy( final ConnectionKeepAliveStrategy keepAliveStrategy) { this.keepAliveStrategy = keepAliveStrategy; return this; } /** * Assigns {@link AuthenticationStrategy} instance for target * host authentication. */ public final HttpClientBuilder setTargetAuthenticationStrategy( final AuthenticationStrategy targetAuthStrategy) { this.targetAuthStrategy = targetAuthStrategy; return this; } /** * Assigns {@link AuthenticationStrategy} instance for proxy * authentication. */ public final HttpClientBuilder setProxyAuthenticationStrategy( final AuthenticationStrategy proxyAuthStrategy) { this.proxyAuthStrategy = proxyAuthStrategy; return this; } /** * Assigns {@link UserTokenHandler} instance. *

* Please note this value can be overridden by the {@link #disableConnectionState()} * method. *

*/ public final HttpClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) { this.userTokenHandler = userTokenHandler; return this; } /** * Disables connection state tracking. */ public final HttpClientBuilder disableConnectionState() { connectionStateDisabled = true; return this; } /** * Assigns {@link SchemePortResolver} instance. */ public final HttpClientBuilder setSchemePortResolver( final SchemePortResolver schemePortResolver) { this.schemePortResolver = schemePortResolver; return this; } /** * Assigns {@code User-Agent} value. */ public final HttpClientBuilder setUserAgent(final String userAgent) { this.userAgent = userAgent; return this; } /** * Assigns default request header values. */ public final HttpClientBuilder setDefaultHeaders(final Collection defaultHeaders) { this.defaultHeaders = defaultHeaders; return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. */ public final HttpClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (responseInterceptors == null) { responseInterceptors = new LinkedList<>(); } responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.FIRST, interceptor)); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. */ public final HttpClientBuilder addResponseInterceptorLast(final HttpResponseInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (responseInterceptors == null) { responseInterceptors = new LinkedList<>(); } responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.LAST, interceptor)); return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. */ public final HttpClientBuilder addRequestInterceptorFirst(final HttpRequestInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (requestInterceptors == null) { requestInterceptors = new LinkedList<>(); } requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.FIRST, interceptor)); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. */ public final HttpClientBuilder addRequestInterceptorLast(final HttpRequestInterceptor interceptor) { Args.notNull(interceptor, "Interceptor"); if (requestInterceptors == null) { requestInterceptors = new LinkedList<>(); } requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.LAST, interceptor)); return this; } /** * Adds this execution interceptor before an existing interceptor. */ public final HttpClientBuilder addExecInterceptorBefore(final String existing, final String name, final ExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.BEFORE, name, interceptor, existing)); return this; } /** * Adds this execution interceptor after interceptor with the given name. */ public final HttpClientBuilder addExecInterceptorAfter(final String existing, final String name, final ExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notBlank(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.AFTER, name, interceptor, existing)); return this; } /** * Replace an existing interceptor with the given name with new interceptor. */ public final HttpClientBuilder replaceExecInterceptor(final String existing, final ExecChainHandler interceptor) { Args.notBlank(existing, "Existing"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.REPLACE, existing, interceptor, existing)); return this; } /** * Add an interceptor to the head of the processing list. */ public final HttpClientBuilder addExecInterceptorFirst(final String name, final ExecChainHandler interceptor) { Args.notNull(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.FIRST, name, interceptor, null)); return this; } /** * Add an interceptor to the tail of the processing list. */ public final HttpClientBuilder addExecInterceptorLast(final String name, final ExecChainHandler interceptor) { Args.notNull(name, "Name"); Args.notNull(interceptor, "Interceptor"); if (execInterceptors == null) { execInterceptors = new LinkedList<>(); } execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.LAST, name, interceptor, null)); return this; } /** * Disables state (cookie) management. */ public final HttpClientBuilder disableCookieManagement() { this.cookieManagementDisabled = true; return this; } /** * Disables automatic content decompression. */ public final HttpClientBuilder disableContentCompression() { contentCompressionDisabled = true; return this; } /** * Disables authentication scheme caching. */ public final HttpClientBuilder disableAuthCaching() { this.authCachingDisabled = true; return this; } /** * Assigns {@link HttpRequestRetryStrategy} instance. *

* Please note this value can be overridden by the {@link #disableAutomaticRetries()} * method. */ public final HttpClientBuilder setRetryStrategy(final HttpRequestRetryStrategy retryStrategy) { this.retryStrategy = retryStrategy; return this; } /** * Disables automatic request recovery and re-execution. */ public final HttpClientBuilder disableAutomaticRetries() { automaticRetriesDisabled = true; return this; } /** * Assigns default proxy value. *

* Please note this value can be overridden by the {@link #setRoutePlanner( * org.apache.hc.client5.http.routing.HttpRoutePlanner)} method. */ public final HttpClientBuilder setProxy(final HttpHost proxy) { this.proxy = proxy; return this; } /** * Assigns {@link HttpRoutePlanner} instance. */ public final HttpClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) { this.routePlanner = routePlanner; return this; } /** * Assigns {@link RedirectStrategy} instance. *

* Please note this value can be overridden by the {@link #disableRedirectHandling()} * method. *

` */ public final HttpClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) { this.redirectStrategy = redirectStrategy; return this; } /** * Disables automatic redirect handling. */ public final HttpClientBuilder disableRedirectHandling() { redirectHandlingDisabled = true; return this; } /** * Assigns {@link ConnectionBackoffStrategy} instance. */ public final HttpClientBuilder setConnectionBackoffStrategy( final ConnectionBackoffStrategy connectionBackoffStrategy) { this.connectionBackoffStrategy = connectionBackoffStrategy; return this; } /** * Assigns {@link BackoffManager} instance. */ public final HttpClientBuilder setBackoffManager(final BackoffManager backoffManager) { this.backoffManager = backoffManager; 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 HttpClientBuilder 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 HttpClientBuilder setDefaultCredentialsProvider( final CredentialsProvider credentialsProvider) { this.credentialsProvider = credentialsProvider; return this; } /** * Assigns default {@link org.apache.hc.client5.http.auth.AuthScheme} registry which will * be used for request execution if not explicitly set in the client execution * context. */ public final HttpClientBuilder setDefaultAuthSchemeRegistry( final Lookup authSchemeRegistry) { this.authSchemeRegistry = authSchemeRegistry; return this; } /** * Assigns default {@link org.apache.hc.client5.http.cookie.CookieSpec} registry which will * be used for request execution if not explicitly set in the client execution * context. * * @see CookieSpecSupport * */ public final HttpClientBuilder setDefaultCookieSpecRegistry( final Lookup cookieSpecRegistry) { this.cookieSpecRegistry = cookieSpecRegistry; return this; } /** * Assigns a map of {@link org.apache.hc.client5.http.entity.InputStreamFactory}s * to be used for automatic content decompression. */ public final HttpClientBuilder setContentDecoderRegistry( final LinkedHashMap contentDecoderMap) { this.contentDecoderMap = contentDecoderMap; 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 HttpClientBuilder setDefaultRequestConfig(final RequestConfig config) { this.defaultRequestConfig = config; return this; } /** * Use system properties when creating and configuring default * implementations. */ public final HttpClientBuilder useSystemProperties() { this.systemProperties = true; return this; } /** * Makes this instance of HttpClient proactively evict expired connections from the * connection pool using a background thread. *

* One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order * to stop and release the background thread. *

* Please note this method has no effect if the instance of HttpClient is configured to * use a shared connection manager. * * @see #setConnectionManagerShared(boolean) * @see ConnPoolControl#closeExpired() * * @since 4.4 */ public final HttpClientBuilder evictExpiredConnections() { evictExpiredConnections = true; return this; } /** * Makes this instance of HttpClient proactively evict idle connections from the * connection pool using a background thread. *

* One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order * to stop and release the background thread. *

* Please note this method has no effect if the instance of HttpClient is configured to * use a shared connection manager. * * @see #setConnectionManagerShared(boolean) * @see ConnPoolControl#closeIdle(TimeValue) * * @param maxIdleTime maximum time persistent connections can stay idle while kept alive * in the connection pool. Connections whose inactivity period exceeds this value will * get closed and evicted from the pool. * * @since 4.4 */ public final HttpClientBuilder evictIdleConnections(final TimeValue maxIdleTime) { this.evictIdleConnections = true; this.maxIdleTime = maxIdleTime; return this; } /** * Disables the default user agent set by this builder if none has been provided by the user. * * @since 4.5.7 */ public final HttpClientBuilder disableDefaultUserAgent() { this.defaultUserAgentDisabled = true; return this; } /** * Request exec chain customization and extension. *

* For internal use. */ @Internal protected void customizeExecChain(final NamedElementChain execChainDefinition) { } /** * Adds to the list of {@link Closeable} resources to be managed by the client. *

* For internal use. */ @Internal protected void addCloseable(final Closeable closeable) { if (closeable == null) { return; } if (closeables == null) { closeables = new ArrayList<>(); } closeables.add(closeable); } public CloseableHttpClient build() { // Create main request executor // We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version HttpRequestExecutor requestExecCopy = this.requestExec; if (requestExecCopy == null) { requestExecCopy = new HttpRequestExecutor(); } HttpClientConnectionManager connManagerCopy = this.connManager; if (connManagerCopy == null) { connManagerCopy = PoolingHttpClientConnectionManagerBuilder.create().build(); } ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy; if (reuseStrategyCopy == null) { if (systemProperties) { final String s = System.getProperty("http.keepAlive", "true"); if ("true".equalsIgnoreCase(s)) { reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE; } else { reuseStrategyCopy = (request, response, context) -> false; } } else { reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE; } } ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy; if (keepAliveStrategyCopy == null) { keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE; } AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy; if (targetAuthStrategyCopy == null) { targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy; if (proxyAuthStrategyCopy == null) { proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } UserTokenHandler userTokenHandlerCopy = this.userTokenHandler; if (userTokenHandlerCopy == null) { if (!connectionStateDisabled) { userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE; } else { userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE; } } String userAgentCopy = this.userAgent; if (userAgentCopy == null) { if (systemProperties) { userAgentCopy = System.getProperty("http.agent"); } if (userAgentCopy == null && !defaultUserAgentDisabled) { userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpClient", "org.apache.hc.client5", getClass()); } } final HttpProcessorBuilder b = HttpProcessorBuilder.create(); if (requestInterceptors != null) { for (final RequestInterceptorEntry entry: requestInterceptors) { if (entry.position == RequestInterceptorEntry.Position.FIRST) { b.addFirst(entry.interceptor); } } } if (responseInterceptors != null) { for (final ResponseInterceptorEntry entry: responseInterceptors) { if (entry.position == ResponseInterceptorEntry.Position.FIRST) { b.addFirst(entry.interceptor); } } } b.addAll( new RequestDefaultHeaders(defaultHeaders), new RequestContent(), new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent(userAgentCopy), new RequestExpectContinue()); if (!cookieManagementDisabled) { b.add(RequestAddCookies.INSTANCE); } if (!cookieManagementDisabled) { b.add(ResponseProcessCookies.INSTANCE); } if (requestInterceptors != null) { for (final RequestInterceptorEntry entry: requestInterceptors) { if (entry.position == RequestInterceptorEntry.Position.LAST) { b.addLast(entry.interceptor); } } } if (responseInterceptors != null) { for (final ResponseInterceptorEntry entry: responseInterceptors) { if (entry.position == ResponseInterceptorEntry.Position.LAST) { b.addLast(entry.interceptor); } } } final HttpProcessor httpProcessor = b.build(); final NamedElementChain execChainDefinition = new NamedElementChain<>(); execChainDefinition.addLast( new MainClientExec(connManagerCopy, httpProcessor, reuseStrategyCopy, keepAliveStrategyCopy, userTokenHandlerCopy), ChainElement.MAIN_TRANSPORT.name()); execChainDefinition.addFirst( new ConnectExec( reuseStrategyCopy, new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)), proxyAuthStrategyCopy, schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE, authCachingDisabled), ChainElement.CONNECT.name()); execChainDefinition.addFirst( new ProtocolExec( targetAuthStrategyCopy, proxyAuthStrategyCopy, schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE, authCachingDisabled), ChainElement.PROTOCOL.name()); // Add request retry executor, if not disabled if (!automaticRetriesDisabled) { HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy; if (retryStrategyCopy == null) { retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE; } execChainDefinition.addFirst( new HttpRequestRetryExec(retryStrategyCopy), ChainElement.RETRY.name()); } HttpRoutePlanner routePlannerCopy = this.routePlanner; if (routePlannerCopy == null) { SchemePortResolver schemePortResolverCopy = this.schemePortResolver; if (schemePortResolverCopy == null) { schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE; } if (proxy != null) { routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy); } else if (systemProperties) { routePlannerCopy = new SystemDefaultRoutePlanner( schemePortResolverCopy, ProxySelector.getDefault()); } else { routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy); } } if (!contentCompressionDisabled) { if (contentDecoderMap != null) { final List encodings = new ArrayList<>(contentDecoderMap.keySet()); final RegistryBuilder b2 = RegistryBuilder.create(); for (final Map.Entry entry: contentDecoderMap.entrySet()) { b2.register(entry.getKey(), entry.getValue()); } final Registry decoderRegistry = b2.build(); execChainDefinition.addFirst( new ContentCompressionExec(encodings, decoderRegistry, true), ChainElement.COMPRESS.name()); } else { execChainDefinition.addFirst(new ContentCompressionExec(true), ChainElement.COMPRESS.name()); } } // Add redirect executor, if not disabled if (!redirectHandlingDisabled) { RedirectStrategy redirectStrategyCopy = this.redirectStrategy; if (redirectStrategyCopy == null) { redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE; } execChainDefinition.addFirst( new RedirectExec(routePlannerCopy, redirectStrategyCopy), ChainElement.REDIRECT.name()); } // Optionally, add connection back-off executor if (this.backoffManager != null && this.connectionBackoffStrategy != null) { execChainDefinition.addFirst(new BackoffStrategyExec(this.connectionBackoffStrategy, this.backoffManager), ChainElement.BACK_OFF.name()); } if (execInterceptors != null) { for (final ExecInterceptorEntry entry: execInterceptors) { switch (entry.position) { case AFTER: execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name); break; case BEFORE: execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name); break; case REPLACE: execChainDefinition.replace(entry.existing, entry.interceptor); break; case FIRST: execChainDefinition.addFirst(entry.interceptor, entry.name); break; case LAST: // Don't add last, after MainClientExec, as that does not delegate to the chain // Instead, add the interceptor just before it, making it effectively the last interceptor execChainDefinition.addBefore(ChainElement.MAIN_TRANSPORT.name(), entry.interceptor, entry.name); break; } } } customizeExecChain(execChainDefinition); NamedElementChain.Node current = execChainDefinition.getLast(); ExecChainElement execChain = null; while (current != null) { execChain = new ExecChainElement(current.getValue(), execChain); current = current.getPrevious(); } Lookup authSchemeRegistryCopy = this.authSchemeRegistry; if (authSchemeRegistryCopy == null) { authSchemeRegistryCopy = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE) .register(StandardAuthScheme.NTLM, NTLMSchemeFactory.INSTANCE) .register(StandardAuthScheme.SPNEGO, SPNegoSchemeFactory.DEFAULT) .register(StandardAuthScheme.KERBEROS, KerberosSchemeFactory.DEFAULT) .build(); } Lookup cookieSpecRegistryCopy = this.cookieSpecRegistry; if (cookieSpecRegistryCopy == null) { cookieSpecRegistryCopy = CookieSpecSupport.createDefault(); } 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(); } } List closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null; if (!this.connManagerShared) { if (closeablesCopy == null) { closeablesCopy = new ArrayList<>(1); } if (evictExpiredConnections || evictIdleConnections) { if (connManagerCopy instanceof ConnPoolControl) { final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl) connManagerCopy, maxIdleTime, maxIdleTime); closeablesCopy.add(() -> { connectionEvictor.shutdown(); try { connectionEvictor.awaitTermination(Timeout.ofSeconds(1)); } catch (final InterruptedException interrupted) { Thread.currentThread().interrupt(); } }); connectionEvictor.start(); } } closeablesCopy.add(connManagerCopy); } return new InternalHttpClient( connManagerCopy, requestExecCopy, execChain, routePlannerCopy, cookieSpecRegistryCopy, authSchemeRegistryCopy, defaultCookieStore, defaultCredentialsProvider, defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT, closeablesCopy); } } HttpClients.java000066400000000000000000000053711434266521000362710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.io.HttpClientConnectionManager; /** * Factory methods for {@link CloseableHttpClient} instances. * * @since 4.3 */ public final class HttpClients { private HttpClients() { super(); } /** * Creates builder object for construction of custom * {@link CloseableHttpClient} instances. */ public static HttpClientBuilder custom() { return HttpClientBuilder.create(); } /** * Creates {@link CloseableHttpClient} instance with default * configuration. */ public static CloseableHttpClient createDefault() { return HttpClientBuilder.create().build(); } /** * Creates {@link CloseableHttpClient} instance with default * configuration based on system properties. */ public static CloseableHttpClient createSystem() { return HttpClientBuilder.create().useSystemProperties().build(); } /** * Creates {@link CloseableHttpClient} instance that implements * the most basic HTTP protocol support. */ public static MinimalHttpClient createMinimal() { return new MinimalHttpClient(new PoolingHttpClientConnectionManager()); } /** * Creates {@link CloseableHttpClient} instance that implements * the most basic HTTP protocol support. */ public static MinimalHttpClient createMinimal(final HttpClientConnectionManager connManager) { return new MinimalHttpClient(connManager); } } HttpRequestFutureTask.java000066400000000000000000000065331434266521000403370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.util.concurrent.FutureTask; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.http.ClassicHttpRequest; final class HttpRequestFutureTask extends FutureTask { private final ClassicHttpRequest request; private final HttpRequestTaskCallable callable; HttpRequestFutureTask( final ClassicHttpRequest request, final HttpRequestTaskCallable httpCallable) { super(httpCallable); this.request = request; this.callable = httpCallable; } @Override public boolean cancel(final boolean mayInterruptIfRunning) { callable.cancel(); if (mayInterruptIfRunning && request instanceof Cancellable) { ((Cancellable) request).cancel(); } return super.cancel(mayInterruptIfRunning); } /** * @return the time in millis the task was scheduled. */ public long scheduledTime() { return callable.getScheduled(); } /** * @return the time in millis the task was started. */ public long startedTime() { return callable.getStarted(); } /** * @return the time in millis the task was finished/cancelled. */ public long endedTime() { if (isDone()) { return callable.getEnded(); } else { throw new IllegalStateException("Task is not done yet"); } } /** * @return the time in millis it took to make the request (excluding the time it was * scheduled to be executed). */ public long requestDuration() { if (isDone()) { return endedTime() - startedTime(); } else { throw new IllegalStateException("Task is not done yet"); } } /** * @return the time in millis it took to execute the task from the moment it was scheduled. */ public long taskDuration() { if (isDone()) { return endedTime() - scheduledTime(); } else { throw new IllegalStateException("Task is not done yet"); } } @Override public String toString() { return request.toString(); } }HttpRequestRetryExec.java000066400000000000000000000200211434266521000401400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InterruptedIOException; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChain.Scope; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.NoHttpResponseException; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request executor in the request execution chain that is responsible for * making a decision whether a request that failed due to an I/O exception * or received a specific response from the target server should * be re-executed. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public class HttpRequestRetryExec implements ExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(HttpRequestRetryExec.class); private final HttpRequestRetryStrategy retryStrategy; public HttpRequestRetryExec( final HttpRequestRetryStrategy retryStrategy) { Args.notNull(retryStrategy, "retryStrategy"); this.retryStrategy = retryStrategy; } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(request, "request"); Args.notNull(scope, "scope"); final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final HttpClientContext context = scope.clientContext; ClassicHttpRequest currentRequest = request; for (int execCount = 1;; execCount++) { final ClassicHttpResponse response; try { response = chain.proceed(currentRequest, scope); } catch (final IOException ex) { if (scope.execRuntime.isExecutionAborted()) { throw new RequestFailedException("Request aborted"); } final HttpEntity requestEntity = request.getEntity(); if (requestEntity != null && !requestEntity.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} cannot retry non-repeatable request", exchangeId); } throw ex; } if (retryStrategy.retryRequest(request, ex, execCount, context)) { if (LOG.isDebugEnabled()) { LOG.debug("{} {}", exchangeId, ex.getMessage(), ex); } if (LOG.isInfoEnabled()) { LOG.info("Recoverable I/O exception ({}) caught when processing request to {}", ex.getClass().getName(), route); } final TimeValue nextInterval = retryStrategy.getRetryInterval(request, ex, execCount, context); if (TimeValue.isPositive(nextInterval)) { try { if (LOG.isDebugEnabled()) { LOG.debug("{} wait for {}", exchangeId, nextInterval); } nextInterval.sleep(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new InterruptedIOException(); } } currentRequest = ClassicRequestBuilder.copy(scope.originalRequest).build(); continue; } else { if (ex instanceof NoHttpResponseException) { final NoHttpResponseException updatedex = new NoHttpResponseException( route.getTargetHost().toHostString() + " failed to respond"); updatedex.setStackTrace(ex.getStackTrace()); throw updatedex; } throw ex; } } try { final HttpEntity entity = request.getEntity(); if (entity != null && !entity.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} cannot retry non-repeatable request", exchangeId); } return response; } if (retryStrategy.retryRequest(response, execCount, context)) { final TimeValue nextInterval = retryStrategy.getRetryInterval(response, execCount, context); // Make sure the retry interval does not exceed the response timeout if (TimeValue.isPositive(nextInterval)) { final RequestConfig requestConfig = context.getRequestConfig(); final Timeout responseTimeout = requestConfig.getResponseTimeout(); if (responseTimeout != null && nextInterval.compareTo(responseTimeout) > 0) { return response; } } response.close(); if (TimeValue.isPositive(nextInterval)) { try { if (LOG.isDebugEnabled()) { LOG.debug("{} wait for {}", exchangeId, nextInterval); } nextInterval.sleep(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new InterruptedIOException(); } } currentRequest = ClassicRequestBuilder.copy(scope.originalRequest).build(); } else { return response; } } catch (final RuntimeException ex) { response.close(); throw ex; } } } } HttpRequestTaskCallable.java000066400000000000000000000104251434266521000405570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.http.protocol.HttpContext; class HttpRequestTaskCallable implements Callable { private final ClassicHttpRequest request; private final HttpClient httpclient; private final AtomicBoolean cancelled = new AtomicBoolean(false); private final long scheduled = System.currentTimeMillis(); private long started = -1; private long ended = -1; private final HttpContext context; private final HttpClientResponseHandler responseHandler; private final FutureCallback callback; private final FutureRequestExecutionMetrics metrics; HttpRequestTaskCallable( final HttpClient httpClient, final ClassicHttpRequest request, final HttpContext context, final HttpClientResponseHandler responseHandler, final FutureCallback callback, final FutureRequestExecutionMetrics metrics) { this.httpclient = httpClient; this.responseHandler = responseHandler; this.request = request; this.context = context; this.callback = callback; this.metrics = metrics; } public long getScheduled() { return scheduled; } public long getStarted() { return started; } public long getEnded() { return ended; } @Override public V call() throws Exception { if (!cancelled.get()) { try { metrics.getActiveConnections().incrementAndGet(); started = System.currentTimeMillis(); try { metrics.getScheduledConnections().decrementAndGet(); final V result = httpclient.execute(request, context, responseHandler); ended = System.currentTimeMillis(); metrics.getSuccessfulConnections().increment(started); if (callback != null) { callback.completed(result); } return result; } catch (final Exception e) { metrics.getFailedConnections().increment(started); ended = System.currentTimeMillis(); if (callback != null) { callback.failed(e); } throw e; } } finally { metrics.getRequests().increment(started); metrics.getTasks().increment(started); metrics.getActiveConnections().decrementAndGet(); } } throw new CancellationException(); } public void cancel() { cancelled.set(true); if (callback != null) { callback.cancelled(); } } }InternalExecRuntime.java000066400000000000000000000257451434266521000377640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; class InternalExecRuntime implements ExecRuntime, Cancellable { private final Logger log; private final HttpClientConnectionManager manager; private final HttpRequestExecutor requestExecutor; private final CancellableDependency cancellableDependency; private final AtomicReference endpointRef; private volatile boolean reusable; private volatile Object state; private volatile TimeValue validDuration; InternalExecRuntime( final Logger log, final HttpClientConnectionManager manager, final HttpRequestExecutor requestExecutor, final CancellableDependency cancellableDependency) { super(); this.log = log; this.manager = manager; this.requestExecutor = requestExecutor; this.cancellableDependency = cancellableDependency; this.endpointRef = new AtomicReference<>(); this.validDuration = TimeValue.NEG_ONE_MILLISECOND; } @Override public boolean isExecutionAborted() { return cancellableDependency != null && cancellableDependency.isCancelled(); } @Override public boolean isEndpointAcquired() { return endpointRef.get() != null; } @Override public void acquireEndpoint( final String id, final HttpRoute route, final Object object, final HttpClientContext context) throws IOException { Args.notNull(route, "Route"); if (endpointRef.get() == null) { final RequestConfig requestConfig = context.getRequestConfig(); final Timeout connectionRequestTimeout = requestConfig.getConnectionRequestTimeout(); if (log.isDebugEnabled()) { log.debug("{} acquiring endpoint ({})", id, connectionRequestTimeout); } final LeaseRequest connRequest = manager.lease(id, route, connectionRequestTimeout, object); state = object; if (cancellableDependency != null) { cancellableDependency.setDependency(connRequest); } try { final ConnectionEndpoint connectionEndpoint = connRequest.get(connectionRequestTimeout); endpointRef.set(connectionEndpoint); reusable = connectionEndpoint.isConnected(); if (cancellableDependency != null) { cancellableDependency.setDependency(this); } if (log.isDebugEnabled()) { log.debug("{} acquired endpoint {}", id, ConnPoolSupport.getId(connectionEndpoint)); } } catch(final TimeoutException ex) { connRequest.cancel(); throw new ConnectionRequestTimeoutException(ex.getMessage()); } catch(final InterruptedException interrupted) { connRequest.cancel(); Thread.currentThread().interrupt(); throw new RequestFailedException("Request aborted", interrupted); } catch(final ExecutionException ex) { connRequest.cancel(); Throwable cause = ex.getCause(); if (cause == null) { cause = ex; } throw new RequestFailedException("Request execution failed", cause); } } else { throw new IllegalStateException("Endpoint already acquired"); } } ConnectionEndpoint ensureValid() { final ConnectionEndpoint endpoint = endpointRef.get(); if (endpoint == null) { throw new IllegalStateException("Endpoint not acquired / already released"); } return endpoint; } @Override public boolean isEndpointConnected() { final ConnectionEndpoint endpoint = endpointRef.get(); return endpoint != null && endpoint.isConnected(); } private void connectEndpoint(final ConnectionEndpoint endpoint, final HttpClientContext context) throws IOException { if (isExecutionAborted()) { throw new RequestFailedException("Request aborted"); } final RequestConfig requestConfig = context.getRequestConfig(); @SuppressWarnings("deprecation") final Timeout connectTimeout = requestConfig.getConnectTimeout(); if (log.isDebugEnabled()) { log.debug("{} connecting endpoint ({})", ConnPoolSupport.getId(endpoint), connectTimeout); } manager.connect(endpoint, connectTimeout, context); if (log.isDebugEnabled()) { log.debug("{} endpoint connected", ConnPoolSupport.getId(endpoint)); } } @Override public void connectEndpoint(final HttpClientContext context) throws IOException { final ConnectionEndpoint endpoint = ensureValid(); if (!endpoint.isConnected()) { connectEndpoint(endpoint, context); } } @Override public void disconnectEndpoint() throws IOException { final ConnectionEndpoint endpoint = endpointRef.get(); if (endpoint != null) { endpoint.close(); if (log.isDebugEnabled()) { log.debug("{} endpoint closed", ConnPoolSupport.getId(endpoint)); } } } @Override public void upgradeTls(final HttpClientContext context) throws IOException { final ConnectionEndpoint endpoint = ensureValid(); if (log.isDebugEnabled()) { log.debug("{} upgrading endpoint", ConnPoolSupport.getId(endpoint)); } manager.upgrade(endpoint, context); } @Override public ClassicHttpResponse execute( final String id, final ClassicHttpRequest request, final HttpClientContext context) throws IOException, HttpException { final ConnectionEndpoint endpoint = ensureValid(); if (!endpoint.isConnected()) { connectEndpoint(endpoint, context); } if (isExecutionAborted()) { throw new RequestFailedException("Request aborted"); } final RequestConfig requestConfig = context.getRequestConfig(); final Timeout responseTimeout = requestConfig.getResponseTimeout(); if (responseTimeout != null) { endpoint.setSocketTimeout(responseTimeout); } if (log.isDebugEnabled()) { log.debug("{} start execution {}", ConnPoolSupport.getId(endpoint), id); } return endpoint.execute(id, request, requestExecutor, context); } @Override public boolean isConnectionReusable() { return reusable; } @Override public void markConnectionReusable(final Object state, final TimeValue validDuration) { this.reusable = true; this.state = state; this.validDuration = validDuration; } @Override public void markConnectionNonReusable() { reusable = false; } private void discardEndpoint(final ConnectionEndpoint endpoint) { try { endpoint.close(CloseMode.IMMEDIATE); if (log.isDebugEnabled()) { log.debug("{} endpoint closed", ConnPoolSupport.getId(endpoint)); } } finally { if (log.isDebugEnabled()) { log.debug("{} discarding endpoint", ConnPoolSupport.getId(endpoint)); } manager.release(endpoint, null, TimeValue.ZERO_MILLISECONDS); } } @Override public void releaseEndpoint() { final ConnectionEndpoint endpoint = endpointRef.getAndSet(null); if (endpoint != null) { if (reusable) { if (log.isDebugEnabled()) { log.debug("{} releasing valid endpoint", ConnPoolSupport.getId(endpoint)); } manager.release(endpoint, state, validDuration); } else { discardEndpoint(endpoint); } } } @Override public void discardEndpoint() { final ConnectionEndpoint endpoint = endpointRef.getAndSet(null); if (endpoint != null) { discardEndpoint(endpoint); } } @Override public boolean cancel() { final boolean alreadyReleased = endpointRef.get() == null; final ConnectionEndpoint endpoint = endpointRef.getAndSet(null); if (endpoint != null) { if (log.isDebugEnabled()) { log.debug("{} cancel", ConnPoolSupport.getId(endpoint)); } discardEndpoint(endpoint); } return !alreadyReleased; } @Override public ExecRuntime fork(final CancellableDependency cancellableDependency) { return new InternalExecRuntime(log, manager, requestExecutor, cancellableDependency); } } InternalHttpClient.java000066400000000000000000000216311434266521000376000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.Closeable; import java.io.IOException; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Internal implementation of {@link CloseableHttpClient}. *

* Concurrent message exchanges executed by this client will get assigned to * separate connections leased from the connection pool. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) @Internal class InternalHttpClient extends CloseableHttpClient implements Configurable { private static final Logger LOG = LoggerFactory.getLogger(InternalHttpClient.class); private final HttpClientConnectionManager connManager; private final HttpRequestExecutor requestExecutor; private final ExecChainElement execChain; private final HttpRoutePlanner routePlanner; private final Lookup cookieSpecRegistry; private final Lookup authSchemeRegistry; private final CookieStore cookieStore; private final CredentialsProvider credentialsProvider; private final RequestConfig defaultConfig; private final ConcurrentLinkedQueue closeables; public InternalHttpClient( final HttpClientConnectionManager connManager, final HttpRequestExecutor requestExecutor, final ExecChainElement execChain, final HttpRoutePlanner routePlanner, final Lookup cookieSpecRegistry, final Lookup authSchemeRegistry, final CookieStore cookieStore, final CredentialsProvider credentialsProvider, final RequestConfig defaultConfig, final List closeables) { super(); this.connManager = Args.notNull(connManager, "Connection manager"); this.requestExecutor = Args.notNull(requestExecutor, "Request executor"); this.execChain = Args.notNull(execChain, "Execution chain"); this.routePlanner = Args.notNull(routePlanner, "Route planner"); this.cookieSpecRegistry = cookieSpecRegistry; this.authSchemeRegistry = authSchemeRegistry; this.cookieStore = cookieStore; this.credentialsProvider = credentialsProvider; this.defaultConfig = defaultConfig; this.closeables = closeables != null ? new ConcurrentLinkedQueue<>(closeables) : null; } private HttpRoute determineRoute(final HttpHost target, final HttpContext context) throws HttpException { return this.routePlanner.determineRoute(target, context); } private void setupContext(final HttpClientContext context) { 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 protected CloseableHttpResponse doExecute( final HttpHost target, final ClassicHttpRequest request, final HttpContext context) throws IOException { Args.notNull(request, "HTTP request"); try { final HttpClientContext localcontext = HttpClientContext.adapt( context != null ? context : new BasicHttpContext()); RequestConfig config = null; if (request instanceof Configurable) { config = ((Configurable) request).getConfig(); } if (config != null) { localcontext.setRequestConfig(config); } setupContext(localcontext); final HttpRoute route = determineRoute( target != null ? target : RoutingSupport.determineHost(request), localcontext); final String exchangeId = ExecSupport.getNextExchangeId(); localcontext.setExchangeId(exchangeId); if (LOG.isDebugEnabled()) { LOG.debug("{} preparing request execution", exchangeId); } final ExecRuntime execRuntime = new InternalExecRuntime(LOG, connManager, requestExecutor, request instanceof CancellableDependency ? (CancellableDependency) request : null); final ExecChain.Scope scope = new ExecChain.Scope(exchangeId, route, request, execRuntime, localcontext); final ClassicHttpResponse response = this.execChain.execute(ClassicRequestBuilder.copy(request).build(), scope); return CloseableHttpResponse.adapt(response); } catch (final HttpException httpException) { throw new ClientProtocolException(httpException.getMessage(), httpException); } } @Override public RequestConfig getConfig() { return this.defaultConfig; } @Override public void close() { close(CloseMode.GRACEFUL); } @Override public void close(final CloseMode closeMode) { if (this.closeables != null) { Closeable closeable; while ((closeable = this.closeables.poll()) != null) { try { if (closeable instanceof ModalCloseable) { ((ModalCloseable) closeable).close(closeMode); } else { closeable.close(); } } catch (final IOException ex) { LOG.error(ex.getMessage(), ex); } } } } } MainClientExec.java000066400000000000000000000163631434266521000366630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InterruptedIOException; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.impl.ConnectionShutdownException; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Usually the last request execution handler in the classic request execution * chain that is responsible for execution of request / response exchanges with * the opposite endpoint. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class MainClientExec implements ExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(MainClientExec.class); private final HttpClientConnectionManager connectionManager; private final HttpProcessor httpProcessor; private final ConnectionReuseStrategy reuseStrategy; private final ConnectionKeepAliveStrategy keepAliveStrategy; private final UserTokenHandler userTokenHandler; /** * @since 4.4 */ public MainClientExec( final HttpClientConnectionManager connectionManager, final HttpProcessor httpProcessor, final ConnectionReuseStrategy reuseStrategy, final ConnectionKeepAliveStrategy keepAliveStrategy, final UserTokenHandler userTokenHandler) { this.connectionManager = Args.notNull(connectionManager, "Connection manager"); this.httpProcessor = Args.notNull(httpProcessor, "HTTP protocol processor"); this.reuseStrategy = Args.notNull(reuseStrategy, "Connection reuse strategy"); this.keepAliveStrategy = Args.notNull(keepAliveStrategy, "Connection keep alive strategy"); this.userTokenHandler = Args.notNull(userTokenHandler, "User token handler"); } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final HttpClientContext context = scope.clientContext; final ExecRuntime execRuntime = scope.execRuntime; if (LOG.isDebugEnabled()) { LOG.debug("{} executing {}", exchangeId, new RequestLine(request)); } try { // Run request protocol interceptors context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); httpProcessor.process(request, request.getEntity(), context); final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, context); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, response.getEntity(), context); Object userToken = context.getUserToken(); if (userToken == null) { userToken = userTokenHandler.getUserToken(route, request, context); context.setAttribute(HttpClientContext.USER_TOKEN, userToken); } // The connection is in or can be brought to a re-usable state. if (reuseStrategy.keepAlive(request, response, context)) { // Set the idle duration of this connection final TimeValue duration = keepAliveStrategy.getKeepAliveDuration(response, context); if (LOG.isDebugEnabled()) { final String s; if (duration != null) { s = "for " + duration; } else { s = "indefinitely"; } LOG.debug("{} connection can be kept alive {}", exchangeId, s); } execRuntime.markConnectionReusable(userToken, duration); } else { execRuntime.markConnectionNonReusable(); } // check for entity, release connection if possible final HttpEntity entity = response.getEntity(); if (entity == null || !entity.isStreaming()) { // connection not needed and (assumed to be) in re-usable state execRuntime.releaseEndpoint(); return new CloseableHttpResponse(response, null); } return new CloseableHttpResponse(response, execRuntime); } catch (final ConnectionShutdownException ex) { final InterruptedIOException ioex = new InterruptedIOException( "Connection has been shut down"); ioex.initCause(ex); execRuntime.discardEndpoint(); throw ioex; } catch (final HttpException | RuntimeException | IOException ex) { execRuntime.discardEndpoint(); throw ex; } catch (final Error error) { connectionManager.close(CloseMode.IMMEDIATE); throw error; } } } MinimalHttpClient.java000066400000000000000000000211301434266521000374040ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InterruptedIOException; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.config.Configurable; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.ConnectionShutdownException; import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.RequestClientConnControl; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.VersionInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Minimal implementation of {@link CloseableHttpClient}. This client is * optimized for HTTP/1.1 message transport and does not support advanced * HTTP protocol functionality such as request execution via a proxy, state * management, authentication and request redirects. *

* Concurrent message exchanges executed by this client will get assigned to * separate connections leased from the connection pool. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class MinimalHttpClient extends CloseableHttpClient { private static final Logger LOG = LoggerFactory.getLogger(MinimalHttpClient.class); private final HttpClientConnectionManager connManager; private final ConnectionReuseStrategy reuseStrategy; private final SchemePortResolver schemePortResolver; private final HttpRequestExecutor requestExecutor; private final HttpProcessor httpProcessor; MinimalHttpClient(final HttpClientConnectionManager connManager) { super(); this.connManager = Args.notNull(connManager, "HTTP connection manager"); this.reuseStrategy = DefaultClientConnectionReuseStrategy.INSTANCE; this.schemePortResolver = DefaultSchemePortResolver.INSTANCE; this.requestExecutor = new HttpRequestExecutor(this.reuseStrategy); this.httpProcessor = new DefaultHttpProcessor( new RequestContent(), new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent(VersionInfo.getSoftwareInfo( "Apache-HttpClient", "org.apache.hc.client5", getClass()))); } @Override protected CloseableHttpResponse doExecute( final HttpHost target, final ClassicHttpRequest request, final HttpContext context) throws IOException { Args.notNull(target, "Target host"); Args.notNull(request, "HTTP request"); if (request.getScheme() == null) { request.setScheme(target.getSchemeName()); } if (request.getAuthority() == null) { request.setAuthority(new URIAuthority(target)); } final HttpClientContext clientContext = HttpClientContext.adapt( context != null ? context : new BasicHttpContext()); RequestConfig config = null; if (request instanceof Configurable) { config = ((Configurable) request).getConfig(); } if (config != null) { clientContext.setRequestConfig(config); } final HttpRoute route = new HttpRoute(RoutingSupport.normalize(target, schemePortResolver)); final String exchangeId = ExecSupport.getNextExchangeId(); clientContext.setExchangeId(exchangeId); final ExecRuntime execRuntime = new InternalExecRuntime(LOG, connManager, requestExecutor, request instanceof CancellableDependency ? (CancellableDependency) request : null); try { if (!execRuntime.isEndpointAcquired()) { execRuntime.acquireEndpoint(exchangeId, route, null, clientContext); } if (!execRuntime.isEndpointConnected()) { execRuntime.connectEndpoint(clientContext); } clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request); clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, route); httpProcessor.process(request, request.getEntity(), clientContext); final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, clientContext); httpProcessor.process(response, response.getEntity(), clientContext); if (reuseStrategy.keepAlive(request, response, clientContext)) { execRuntime.markConnectionReusable(null, TimeValue.NEG_ONE_MILLISECOND); } else { execRuntime.markConnectionNonReusable(); } // check for entity, release connection if possible final HttpEntity entity = response.getEntity(); if (entity == null || !entity.isStreaming()) { // connection not needed and (assumed to be) in re-usable state execRuntime.releaseEndpoint(); return new CloseableHttpResponse(response, null); } ResponseEntityProxy.enhance(response, execRuntime); return new CloseableHttpResponse(response, execRuntime); } catch (final ConnectionShutdownException ex) { final InterruptedIOException ioex = new InterruptedIOException("Connection has been shut down"); ioex.initCause(ex); execRuntime.discardEndpoint(); throw ioex; } catch (final HttpException httpException) { execRuntime.discardEndpoint(); throw new ClientProtocolException(httpException); } catch (final RuntimeException | IOException ex) { execRuntime.discardEndpoint(); throw ex; } catch (final Error error) { connManager.close(CloseMode.IMMEDIATE); throw error; } } @Override public void close() throws IOException { this.connManager.close(); } @Override public void close(final CloseMode closeMode) { this.connManager.close(closeMode); } } NullBackoffStrategy.java000066400000000000000000000033471434266521000377420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import org.apache.hc.client5.http.classic.ConnectionBackoffStrategy; import org.apache.hc.core5.http.HttpResponse; /** * This is a {@link ConnectionBackoffStrategy} that never backs off, * for compatibility with existing behavior. * * @since 4.2 */ public class NullBackoffStrategy implements ConnectionBackoffStrategy { @Override public boolean shouldBackoff(final Throwable t) { return false; } @Override public boolean shouldBackoff(final HttpResponse response) { return false; } } ProtocolExec.java000066400000000000000000000340471434266521000364400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.util.Iterator; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.impl.RequestSupport; import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper; import org.apache.hc.client5.http.impl.auth.HttpAuthenticator; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request execution handler in the classic request execution chain * that is responsible for implementation of HTTP specification requirements. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class ProtocolExec implements ExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(ProtocolExec.class); private final AuthenticationStrategy targetAuthStrategy; private final AuthenticationStrategy proxyAuthStrategy; private final HttpAuthenticator authenticator; private final SchemePortResolver schemePortResolver; private final AuthCacheKeeper authCacheKeeper; public ProtocolExec( final AuthenticationStrategy targetAuthStrategy, final AuthenticationStrategy proxyAuthStrategy, final SchemePortResolver schemePortResolver, final boolean authCachingDisabled) { this.targetAuthStrategy = Args.notNull(targetAuthStrategy, "Target authentication strategy"); this.proxyAuthStrategy = Args.notNull(proxyAuthStrategy, "Proxy authentication strategy"); this.authenticator = new HttpAuthenticator(); this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(this.schemePortResolver); } @Override public ClassicHttpResponse execute( final ClassicHttpRequest userRequest, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(userRequest, "HTTP request"); Args.notNull(scope, "Scope"); if (Method.CONNECT.isSame(userRequest.getMethod())) { throw new ProtocolException("Direct execution of CONNECT is not allowed"); } final String exchangeId = scope.exchangeId; final HttpRoute route = scope.route; final HttpClientContext context = scope.clientContext; final ExecRuntime execRuntime = scope.execRuntime; final HttpHost routeTarget = route.getTargetHost(); final HttpHost proxy = route.getProxyHost(); try { final ClassicHttpRequest request; if (proxy != null && !route.isTunnelled()) { final ClassicRequestBuilder requestBuilder = ClassicRequestBuilder.copy(userRequest); if (requestBuilder.getAuthority() == null) { requestBuilder.setAuthority(new URIAuthority(routeTarget)); } requestBuilder.setAbsoluteRequestUri(true); request = requestBuilder.build(); } else { request = userRequest; } // Ensure the request has a scheme and an authority if (request.getScheme() == null) { request.setScheme(routeTarget.getSchemeName()); } if (request.getAuthority() == null) { request.setAuthority(new URIAuthority(routeTarget)); } final URIAuthority authority = request.getAuthority(); if (authority.getUserInfo() != null) { throw new ProtocolException("Request URI authority contains deprecated userinfo component"); } final HttpHost target = new HttpHost( request.getScheme(), authority.getHostName(), schemePortResolver.resolve(request.getScheme(), authority)); final String pathPrefix = RequestSupport.extractPathPrefix(request); final AuthExchange targetAuthExchange = context.getAuthExchange(target); final AuthExchange proxyAuthExchange = proxy != null ? context.getAuthExchange(proxy) : new AuthExchange(); if (!targetAuthExchange.isConnectionBased() && targetAuthExchange.getPathPrefix() != null && !pathPrefix.startsWith(targetAuthExchange.getPathPrefix())) { // force re-authentication if the current path prefix does not match // that of the previous authentication exchange. targetAuthExchange.reset(); } if (targetAuthExchange.getPathPrefix() == null) { targetAuthExchange.setPathPrefix(pathPrefix); } if (authCacheKeeper != null) { authCacheKeeper.loadPreemptively(target, pathPrefix, targetAuthExchange, context); if (proxy != null) { authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, context); } } RequestEntityProxy.enhance(request); for (;;) { if (!request.containsHeader(HttpHeaders.AUTHORIZATION)) { if (LOG.isDebugEnabled()) { LOG.debug("{} target auth state: {}", exchangeId, targetAuthExchange.getState()); } authenticator.addAuthResponse(target, ChallengeType.TARGET, request, targetAuthExchange, context); } if (!request.containsHeader(HttpHeaders.PROXY_AUTHORIZATION) && !route.isTunnelled()) { if (LOG.isDebugEnabled()) { LOG.debug("{} proxy auth state: {}", exchangeId, proxyAuthExchange.getState()); } authenticator.addAuthResponse(proxy, ChallengeType.PROXY, request, proxyAuthExchange, context); } final ClassicHttpResponse response = chain.proceed(request, scope); if (Method.TRACE.isSame(request.getMethod())) { // Do not perform authentication for TRACE request ResponseEntityProxy.enhance(response, execRuntime); return response; } final HttpEntity requestEntity = request.getEntity(); if (requestEntity != null && !requestEntity.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} Cannot retry non-repeatable request", exchangeId); } ResponseEntityProxy.enhance(response, execRuntime); return response; } if (needAuthentication( targetAuthExchange, proxyAuthExchange, proxy != null ? proxy : target, target, pathPrefix, response, context)) { // Make sure the response body is fully consumed, if present final HttpEntity responseEntity = response.getEntity(); if (execRuntime.isConnectionReusable()) { EntityUtils.consume(responseEntity); } else { execRuntime.disconnectEndpoint(); if (proxyAuthExchange.getState() == AuthExchange.State.SUCCESS && proxyAuthExchange.isConnectionBased()) { if (LOG.isDebugEnabled()) { LOG.debug("{} resetting proxy auth state", exchangeId); } proxyAuthExchange.reset(); } if (targetAuthExchange.getState() == AuthExchange.State.SUCCESS && targetAuthExchange.isConnectionBased()) { if (LOG.isDebugEnabled()) { LOG.debug("{} resetting target auth state", exchangeId); } targetAuthExchange.reset(); } } // Reset request headers final ClassicHttpRequest original = scope.originalRequest; request.setHeaders(); for (final Iterator
it = original.headerIterator(); it.hasNext(); ) { request.addHeader(it.next()); } } else { ResponseEntityProxy.enhance(response, execRuntime); return response; } } } catch (final HttpException ex) { execRuntime.discardEndpoint(); throw ex; } catch (final RuntimeException | IOException ex) { execRuntime.discardEndpoint(); for (final AuthExchange authExchange : context.getAuthExchanges().values()) { if (authExchange.isConnectionBased()) { authExchange.reset(); } } throw ex; } } private boolean needAuthentication( final AuthExchange targetAuthExchange, final AuthExchange proxyAuthExchange, final HttpHost proxy, final HttpHost target, final String pathPrefix, final HttpResponse response, final HttpClientContext context) { final RequestConfig config = context.getRequestConfig(); if (config.isAuthenticationEnabled()) { final boolean targetAuthRequested = authenticator.isChallenged( target, ChallengeType.TARGET, response, targetAuthExchange, context); if (authCacheKeeper != null) { if (targetAuthRequested) { authCacheKeeper.updateOnChallenge(target, pathPrefix, targetAuthExchange, context); } else { authCacheKeeper.updateOnNoChallenge(target, pathPrefix, targetAuthExchange, context); } } final boolean proxyAuthRequested = authenticator.isChallenged( proxy, ChallengeType.PROXY, response, proxyAuthExchange, context); if (authCacheKeeper != null) { if (proxyAuthRequested) { authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context); } else { authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context); } } if (targetAuthRequested) { final boolean updated = authenticator.updateAuthState(target, ChallengeType.TARGET, response, targetAuthStrategy, targetAuthExchange, context); if (authCacheKeeper != null) { authCacheKeeper.updateOnResponse(target, pathPrefix, targetAuthExchange, context); } return updated; } if (proxyAuthRequested) { final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response, proxyAuthStrategy, proxyAuthExchange, context); if (authCacheKeeper != null) { authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context); } return updated; } } return false; } } ProxyClient.java000066400000000000000000000237731434266521000363160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.net.Socket; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo.LayerType; import org.apache.hc.client5.http.RouteInfo.TunnelType; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy; import org.apache.hc.client5.http.impl.TunnelRefusedException; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory; import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory; import org.apache.hc.client5.http.impl.auth.HttpAuthenticator; import org.apache.hc.client5.http.impl.auth.KerberosSchemeFactory; import org.apache.hc.client5.http.impl.auth.NTLMSchemeFactory; import org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory; import org.apache.hc.client5.http.impl.io.ManagedHttpClientConnectionFactory; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.RequestClientConnControl; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.DefaultHttpProcessor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.http.protocol.RequestTargetHost; import org.apache.hc.core5.http.protocol.RequestUserAgent; import org.apache.hc.core5.util.Args; /** * ProxyClient can be used to establish a tunnel via an HTTP/1.1 proxy. */ public class ProxyClient { private final HttpConnectionFactory connFactory; private final RequestConfig requestConfig; private final HttpProcessor httpProcessor; private final HttpRequestExecutor requestExec; private final AuthenticationStrategy proxyAuthStrategy; private final HttpAuthenticator authenticator; private final AuthExchange proxyAuthExchange; private final Lookup authSchemeRegistry; private final ConnectionReuseStrategy reuseStrategy; /** * @since 5.0 */ public ProxyClient( final HttpConnectionFactory connFactory, final Http1Config h1Config, final CharCodingConfig charCodingConfig, final RequestConfig requestConfig) { super(); this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.builder() .http1Config(h1Config) .charCodingConfig(charCodingConfig) .build(); this.requestConfig = requestConfig != null ? requestConfig : RequestConfig.DEFAULT; this.httpProcessor = new DefaultHttpProcessor( new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent()); this.requestExec = new HttpRequestExecutor(); this.proxyAuthStrategy = new DefaultAuthenticationStrategy(); this.authenticator = new HttpAuthenticator(); this.proxyAuthExchange = new AuthExchange(); this.authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE) .register(StandardAuthScheme.NTLM, NTLMSchemeFactory.INSTANCE) .register(StandardAuthScheme.SPNEGO, SPNegoSchemeFactory.DEFAULT) .register(StandardAuthScheme.KERBEROS, KerberosSchemeFactory.DEFAULT) .build(); this.reuseStrategy = DefaultClientConnectionReuseStrategy.INSTANCE; } /** * @since 4.3 */ public ProxyClient(final RequestConfig requestConfig) { this(null, null, null, requestConfig); } public ProxyClient() { this(null, null, null, null); } public Socket tunnel( final HttpHost proxy, final HttpHost target, final Credentials credentials) throws IOException, HttpException { Args.notNull(proxy, "Proxy host"); Args.notNull(target, "Target host"); Args.notNull(credentials, "Credentials"); HttpHost host = target; if (host.getPort() <= 0) { host = new HttpHost(host.getSchemeName(), host.getHostName(), 80); } final HttpRoute route = new HttpRoute( host, null, proxy, false, TunnelType.TUNNELLED, LayerType.PLAIN); final ManagedHttpClientConnection conn = this.connFactory.createConnection(null); final HttpContext context = new BasicHttpContext(); ClassicHttpResponse response; final ClassicHttpRequest connect = new BasicClassicHttpRequest(Method.CONNECT, proxy, host.toHostString()); final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(new AuthScope(proxy), credentials); // Populate the execution context context.setAttribute(HttpCoreContext.HTTP_REQUEST, connect); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.CREDS_PROVIDER, credsProvider); context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry); context.setAttribute(HttpClientContext.REQUEST_CONFIG, this.requestConfig); this.requestExec.preProcess(connect, this.httpProcessor, context); for (;;) { if (!conn.isOpen()) { final Socket socket = new Socket(proxy.getHostName(), proxy.getPort()); conn.bind(socket); } this.authenticator.addAuthResponse(proxy, ChallengeType.PROXY, connect, this.proxyAuthExchange, context); response = this.requestExec.execute(connect, conn, context); final int status = response.getCode(); if (status < 200) { throw new HttpException("Unexpected response to CONNECT request: " + response); } if (this.authenticator.isChallenged(proxy, ChallengeType.PROXY, response, this.proxyAuthExchange, context)) { if (this.authenticator.updateAuthState(proxy, ChallengeType.PROXY, response, this.proxyAuthStrategy, this.proxyAuthExchange, context)) { // Retry request if (this.reuseStrategy.keepAlive(connect, response, context)) { // Consume response content final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } else { conn.close(); } // discard previous auth header connect.removeHeaders(HttpHeaders.PROXY_AUTHORIZATION); } else { break; } } else { break; } } final int status = response.getCode(); if (status > 299) { // Buffer response content final HttpEntity entity = response.getEntity(); final String responseMessage = entity != null ? EntityUtils.toString(entity) : null; conn.close(); throw new TunnelRefusedException("CONNECT refused by proxy: " + new StatusLine(response), responseMessage); } return conn.getSocket(); } } RedirectExec.java000066400000000000000000000253021434266521000363720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.net.URI; import java.util.Objects; import org.apache.hc.client5.http.CircularRedirectException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RedirectException; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.RedirectLocations; import org.apache.hc.client5.http.protocol.RedirectStrategy; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request execution handler in the classic request execution chain * responsible for handling of request redirects. *

* Further responsibilities such as communication with the opposite * endpoint is delegated to the next executor in the request execution * chain. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public final class RedirectExec implements ExecChainHandler { private static final Logger LOG = LoggerFactory.getLogger(RedirectExec.class); private final RedirectStrategy redirectStrategy; private final HttpRoutePlanner routePlanner; public RedirectExec( final HttpRoutePlanner routePlanner, final RedirectStrategy redirectStrategy) { super(); Args.notNull(routePlanner, "HTTP route planner"); Args.notNull(redirectStrategy, "HTTP redirect strategy"); this.routePlanner = routePlanner; this.redirectStrategy = redirectStrategy; } @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(scope, "Scope"); final HttpClientContext context = scope.clientContext; RedirectLocations redirectLocations = context.getRedirectLocations(); if (redirectLocations == null) { redirectLocations = new RedirectLocations(); context.setAttribute(HttpClientContext.REDIRECT_LOCATIONS, redirectLocations); } redirectLocations.clear(); final RequestConfig config = context.getRequestConfig(); final int maxRedirects = config.getMaxRedirects() > 0 ? config.getMaxRedirects() : 50; ClassicHttpRequest currentRequest = request; ExecChain.Scope currentScope = scope; for (int redirectCount = 0;;) { final String exchangeId = currentScope.exchangeId; final ClassicHttpResponse response = chain.proceed(currentRequest, currentScope); try { if (config.isRedirectsEnabled() && this.redirectStrategy.isRedirected(request, response, context)) { final HttpEntity requestEntity = request.getEntity(); if (requestEntity != null && !requestEntity.isRepeatable()) { if (LOG.isDebugEnabled()) { LOG.debug("{} cannot redirect non-repeatable request", exchangeId); } return response; } if (redirectCount >= maxRedirects) { throw new RedirectException("Maximum redirects ("+ maxRedirects + ") exceeded"); } redirectCount++; final URI redirectUri = this.redirectStrategy.getLocationURI(currentRequest, response, context); if (LOG.isDebugEnabled()) { LOG.debug("{} redirect requested to location '{}'", exchangeId, redirectUri); } final HttpHost newTarget = URIUtils.extractHost(redirectUri); if (newTarget == null) { throw new ProtocolException("Redirect URI does not specify a valid host name: " + redirectUri); } if (!config.isCircularRedirectsAllowed()) { if (redirectLocations.contains(redirectUri)) { throw new CircularRedirectException("Circular redirect to '" + redirectUri + "'"); } } redirectLocations.add(redirectUri); final int statusCode = response.getCode(); final ClassicRequestBuilder redirectBuilder; switch (statusCode) { case HttpStatus.SC_MOVED_PERMANENTLY: case HttpStatus.SC_MOVED_TEMPORARILY: if (Method.POST.isSame(request.getMethod())) { redirectBuilder = ClassicRequestBuilder.get(); } else { redirectBuilder = ClassicRequestBuilder.copy(scope.originalRequest); } break; case HttpStatus.SC_SEE_OTHER: if (!Method.GET.isSame(request.getMethod()) && !Method.HEAD.isSame(request.getMethod())) { redirectBuilder = ClassicRequestBuilder.get(); } else { redirectBuilder = ClassicRequestBuilder.copy(scope.originalRequest); } break; default: redirectBuilder = ClassicRequestBuilder.copy(scope.originalRequest); } redirectBuilder.setUri(redirectUri); final HttpRoute currentRoute = currentScope.route; if (!Objects.equals(currentRoute.getTargetHost(), newTarget)) { final HttpRoute newRoute = this.routePlanner.determineRoute(newTarget, context); if (!Objects.equals(currentRoute, newRoute)) { if (LOG.isDebugEnabled()) { LOG.debug("{} new route required", exchangeId); } final AuthExchange targetAuthExchange = context.getAuthExchange(currentRoute.getTargetHost()); if (LOG.isDebugEnabled()) { LOG.debug("{} resetting target auth state", exchangeId); } targetAuthExchange.reset(); if (currentRoute.getProxyHost() != null) { final AuthExchange proxyAuthExchange = context.getAuthExchange(currentRoute.getProxyHost()); if (proxyAuthExchange.isConnectionBased()) { if (LOG.isDebugEnabled()) { LOG.debug("{} resetting proxy auth state", exchangeId); } proxyAuthExchange.reset(); } } currentScope = new ExecChain.Scope( currentScope.exchangeId, newRoute, currentScope.originalRequest, currentScope.execRuntime, currentScope.clientContext); } } if (LOG.isDebugEnabled()) { LOG.debug("{} redirecting to '{}' via {}", exchangeId, redirectUri, currentRoute); } currentRequest = redirectBuilder.build(); RequestEntityProxy.enhance(currentRequest); EntityUtils.consume(response.getEntity()); response.close(); } else { return response; } } catch (final RuntimeException | IOException ex) { response.close(); throw ex; } catch (final HttpException ex) { // Protocol exception related to a direct. // The underlying connection may still be salvaged. try { EntityUtils.consume(response.getEntity()); } catch (final IOException ioex) { if (LOG.isDebugEnabled()) { LOG.debug("{} I/O error while releasing connection", exchangeId, ioex); } } finally { response.close(); } throw ex; } } } } RequestAbortedException.java000066400000000000000000000033431434266521000406350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.InterruptedIOException; /** * Signals that the request has been aborted. * * @since 4.3 */ public class RequestAbortedException extends InterruptedIOException { private static final long serialVersionUID = 4973849966012490112L; public RequestAbortedException(final String message) { super(message); } public RequestAbortedException(final String message, final Throwable cause) { super(message); if (cause != null) { initCause(cause); } } } RequestEntityProxy.java000066400000000000000000000073401434266521000377150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Set; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; class RequestEntityProxy implements HttpEntity { static void enhance(final ClassicHttpRequest request) { final HttpEntity entity = request.getEntity(); if (entity != null && !entity.isRepeatable() && !isEnhanced(entity)) { request.setEntity(new RequestEntityProxy(entity)); } } static boolean isEnhanced(final HttpEntity entity) { return entity instanceof RequestEntityProxy; } private final HttpEntity original; private boolean consumed; RequestEntityProxy(final HttpEntity original) { super(); this.original = original; } public HttpEntity getOriginal() { return original; } public boolean isConsumed() { return consumed; } @Override public boolean isRepeatable() { if (!consumed) { return true; } else { return original.isRepeatable(); } } @Override public boolean isChunked() { return original.isChunked(); } @Override public long getContentLength() { return original.getContentLength(); } @Override public String getContentType() { return original.getContentType(); } @Override public String getContentEncoding() { return original.getContentEncoding(); } @Override public InputStream getContent() throws IOException, IllegalStateException { return original.getContent(); } @Override public void writeTo(final OutputStream outStream) throws IOException { consumed = true; original.writeTo(outStream); } @Override public boolean isStreaming() { return original.isStreaming(); } @Override public Supplier> getTrailers() { return original.getTrailers(); } @Override public Set getTrailerNames() { return original.getTrailerNames(); } @Override public void close() throws IOException { original.close(); } @Override public String toString() { final StringBuilder sb = new StringBuilder("RequestEntityProxy{"); sb.append(original); sb.append('}'); return sb.toString(); } } RequestFailedException.java000066400000000000000000000034071434266521000404420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.InterruptedIOException; /** * Signals that the request has been aborted or failed due to an expected condition. * * @since 5.0 */ public class RequestFailedException extends InterruptedIOException { private static final long serialVersionUID = 4973849966012490112L; public RequestFailedException(final String message) { super(message); } public RequestFailedException(final String message, final Throwable cause) { super(message); if (cause != null) { initCause(cause); } } } ResponseEntityProxy.java000066400000000000000000000164021434266521000400620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.SocketException; import java.util.Arrays; import java.util.List; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.impl.io.ChunkedInputStream; import org.apache.hc.core5.http.io.EofSensorInputStream; import org.apache.hc.core5.http.io.EofSensorWatcher; import org.apache.hc.core5.http.io.entity.HttpEntityWrapper; class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher { private final ExecRuntime execRuntime; public static void enhance(final ClassicHttpResponse response, final ExecRuntime execRuntime) { final HttpEntity entity = response.getEntity(); if (entity != null && entity.isStreaming() && execRuntime != null) { response.setEntity(new ResponseEntityProxy(entity, execRuntime)); } } ResponseEntityProxy(final HttpEntity entity, final ExecRuntime execRuntime) { super(entity); this.execRuntime = execRuntime; } private void cleanup() throws IOException { if (this.execRuntime != null) { if (this.execRuntime.isEndpointConnected()) { this.execRuntime.disconnectEndpoint(); } this.execRuntime.discardEndpoint(); } } private void discardConnection() { if (this.execRuntime != null) { this.execRuntime.discardEndpoint(); } } public void releaseConnection() { if (this.execRuntime != null) { this.execRuntime.releaseEndpoint(); } } @Override public boolean isRepeatable() { return false; } @Override public InputStream getContent() throws IOException { return new EofSensorInputStream(super.getContent(), this); } @Override public void writeTo(final OutputStream outStream) throws IOException { try { super.writeTo(outStream != null ? outStream : NullOutputStream.INSTANCE); releaseConnection(); } catch (final IOException | RuntimeException ex) { discardConnection(); throw ex; } finally { cleanup(); } } @Override public boolean eofDetected(final InputStream wrapped) throws IOException { try { // there may be some cleanup required, such as // reading trailers after the response body: if (wrapped != null) { wrapped.close(); } releaseConnection(); } catch (final IOException | RuntimeException ex) { discardConnection(); throw ex; } finally { cleanup(); } return false; } @Override public boolean streamClosed(final InputStream wrapped) throws IOException { try { final boolean open = execRuntime != null && execRuntime.isEndpointAcquired(); // this assumes that closing the stream will // consume the remainder of the response body: try { if (wrapped != null) { wrapped.close(); } releaseConnection(); } catch (final SocketException ex) { if (open) { throw ex; } } } catch (final IOException | RuntimeException ex) { discardConnection(); throw ex; } finally { cleanup(); } return false; } @Override public boolean streamAbort(final InputStream wrapped) throws IOException { cleanup(); return false; } @Override public Supplier> getTrailers() { try { final InputStream underlyingStream = super.getContent(); return () -> { final Header[] footers; if (underlyingStream instanceof ChunkedInputStream) { final ChunkedInputStream chunkedInputStream = (ChunkedInputStream) underlyingStream; footers = chunkedInputStream.getFooters(); } else { footers = new Header[0]; } return Arrays.asList(footers); }; } catch (final IOException e) { throw new IllegalStateException("Unable to retrieve input stream", e); } } @Override public void close() throws IOException { try { // HttpEntity.close will close the underlying resource. Closing a reusable request stream results in // draining remaining data, allowing for connection reuse. super.close(); releaseConnection(); } catch (final IOException | RuntimeException ex) { discardConnection(); throw ex; } finally { cleanup(); } } private static final class NullOutputStream extends OutputStream { private static final NullOutputStream INSTANCE = new NullOutputStream(); private NullOutputStream() {} @Override public void write(@SuppressWarnings("unused") final int byteValue) { // no-op } @Override public void write(@SuppressWarnings("unused") final byte[] buffer) { // no-op } @Override public void write( @SuppressWarnings("unused") final byte[] buffer, @SuppressWarnings("unused") final int off, @SuppressWarnings("unused") final int len) { // no-op } @Override public void flush() { // no-op } @Override public void close() { // no-op } @Override public String toString() { return "NullOutputStream{}"; } } } SystemClock.java000066400000000000000000000025351434266521000362670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; class SystemClock implements Clock { @Override public long getCurrentTime() { return System.currentTimeMillis(); } } package-info.java000066400000000000000000000026771434266521000363620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Classic HTTP client API implementation that supports HTTP/1.1 transport * only. This implementation is mostly API compatible with HttpClient 4.5. * Please use the asynchronous client API implementation for HTTP/2 transport. */ package org.apache.hc.client5.http.impl.classic; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/000077500000000000000000000000001434266521000330665ustar00rootroot00000000000000AbstractCookieAttributeHandler.java000066400000000000000000000037761434266521000417460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieAttributeHandler; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public abstract class AbstractCookieAttributeHandler implements CookieAttributeHandler { @Override public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { // Do nothing } @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { // Always match return true; } } AbstractCookieSpec.java000066400000000000000000000077761434266521000374030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.CookieAttributeHandler; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Asserts; /** * Abstract cookie specification which can delegate the job of parsing, * validation or matching cookie attributes to a number of arbitrary * {@link CookieAttributeHandler}s. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class AbstractCookieSpec implements CookieSpec { /** * Stores attribute name -> attribute handler mappings */ private final Map attribHandlerMap; /** * Default constructor * */ public AbstractCookieSpec() { super(); this.attribHandlerMap = new ConcurrentHashMap<>(10); } /** * @since 4.4 */ protected AbstractCookieSpec(final HashMap map) { super(); Asserts.notNull(map, "Attribute handler map"); this.attribHandlerMap = new ConcurrentHashMap<>(map); } /** * @since 4.4 */ protected AbstractCookieSpec(final CommonCookieAttributeHandler... handlers) { super(); this.attribHandlerMap = new ConcurrentHashMap<>(handlers.length); for (final CommonCookieAttributeHandler handler: handlers) { this.attribHandlerMap.put(handler.getAttributeName(), handler); } } /** * Finds an attribute handler {@link CookieAttributeHandler} for the * given attribute. Returns {@code null} if no attribute handler is * found for the specified attribute. * * @param name attribute name. e.g. Domain, Path, etc. * @return an attribute handler or {@code null} */ protected CookieAttributeHandler findAttribHandler(final String name) { return this.attribHandlerMap.get(name); } /** * Gets attribute handler {@link CookieAttributeHandler} for the * given attribute. * * @param name attribute name. e.g. Domain, Path, etc. * @throws IllegalStateException if handler not found for the * specified attribute. */ protected CookieAttributeHandler getAttribHandler(final String name) { final CookieAttributeHandler handler = findAttribHandler(name); Asserts.check(handler != null, "Handler not registered for " + name + " attribute"); return handler; } protected Collection getAttribHandlers() { return this.attribHandlerMap.values(); } } BasicClientCookie.java000066400000000000000000000250431434266521000371700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.io.Serializable; import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.util.Args; /** * Default implementation of {@link SetCookie}. * * @since 4.0 */ public final class BasicClientCookie implements SetCookie, Cloneable, Serializable { private static final long serialVersionUID = -3869795591041535538L; /** * Default Constructor taking a name and a value. The value may be null. * * @param name The name. * @param value The value. */ public BasicClientCookie(final String name, final String value) { super(); Args.notNull(name, "Name"); this.name = name; this.attribs = new HashMap<>(); this.value = value; } /** * Returns the name. * * @return String name The name */ @Override public String getName() { return this.name; } /** * Returns the value. * * @return String value The current value. */ @Override public String getValue() { return this.value; } /** * Sets the value * * @param value */ @Override public void setValue(final String value) { this.value = value; } /** * Returns the expiration {@link Date} of the cookie, or {@code null} * if none exists. *

Note: the object returned by this method is * considered immutable. Changing it (e.g. using setTime()) could result * in undefined behaviour. Do so at your peril.

* @return Expiration {@link Date}, or {@code null}. * * @see #setExpiryDate(java.util.Date) * * @deprecated Use {@link #getExpiryInstant()} */ @Deprecated @Override public Date getExpiryDate() { return DateUtils.toDate(cookieExpiryDate); } /** * {@inheritDoc} */ @Override public Instant getExpiryInstant() { return cookieExpiryDate; } /** * Sets expiration date. *

Note: the object returned by this method is considered * immutable. Changing it (e.g. using setTime()) could result in undefined * behaviour. Do so at your peril.

* * @param expiryDate the {@link Date} after which this cookie is no longer valid. * * @deprecated Use {{@link #setExpiryDate(Instant)}} * */ @Deprecated @Override public void setExpiryDate (final Date expiryDate) { cookieExpiryDate = DateUtils.toInstant(expiryDate); } /** * Sets expiration date. *

Note: the object returned by this method is considered * immutable. Changing it (e.g. using setTime()) could result in undefined behaviour. Do so at * your peril.

* * @param expiryInstant the {@link Instant} after which this cookie is no longer valid. * @see #getExpiryInstant() * @since 5.2 */ @Override public void setExpiryDate (final Instant expiryInstant) { cookieExpiryDate = expiryInstant; } /** * Returns {@code false} if the cookie should be discarded at the end * of the "session"; {@code true} otherwise. * * @return {@code false} if the cookie should be discarded at the end * of the "session"; {@code true} otherwise */ @Override public boolean isPersistent() { return (null != cookieExpiryDate); } /** * Returns domain attribute of the cookie. * * @return the value of the domain attribute * * @see #setDomain(java.lang.String) */ @Override public String getDomain() { return cookieDomain; } /** * Sets the domain attribute. * * @param domain The value of the domain attribute * * @see #getDomain */ @Override public void setDomain(final String domain) { if (domain != null) { cookieDomain = domain.toLowerCase(Locale.ROOT); } else { cookieDomain = null; } } /** * Returns the path attribute of the cookie * * @return The value of the path attribute. * * @see #setPath(java.lang.String) */ @Override public String getPath() { return cookiePath; } /** * Sets the path attribute. * * @param path The value of the path attribute * * @see #getPath * */ @Override public void setPath(final String path) { cookiePath = path; } /** * @return {@code true} if this cookie should only be sent over secure connections. * @see #setSecure(boolean) */ @Override public boolean isSecure() { return isSecure; } /** * Sets the secure attribute of the cookie. *

* When {@code true} the cookie should only be sent * using a secure protocol (https). This should only be set when * the cookie's originating server used a secure protocol to set the * cookie's value. * * @param secure The value of the secure attribute * * @see #isSecure() */ @Override public void setSecure (final boolean secure) { isSecure = secure; } /** * Sets the http-only attribute of the cookie. * * @param httpOnly true if this cookie is to be marked as * {@code httpOnly}, false otherwise * * @since 5.2 */ @Override public void setHttpOnly(final boolean httpOnly) { this.httpOnly = httpOnly; } /** * Returns true if this cookie has expired. * @param date Current time * * @return {@code true} if the cookie has expired. * * @deprecated Use {@link #isExpired(Instant)} */ @Deprecated @Override public boolean isExpired(final Date date) { Args.notNull(date, "Date"); return (cookieExpiryDate != null && cookieExpiryDate.compareTo(DateUtils.toInstant(date)) <= 0); } /** * Returns true if this cookie has expired. * @param instant Current time * * @return {@code true} if the cookie has expired. */ @Override public boolean isExpired(final Instant instant) { Args.notNull(instant, "Instant"); return (cookieExpiryDate != null && cookieExpiryDate.compareTo(instant) <= 0); } /** * @since 4.4 * * @deprecated Use {@link #getCreationInstant()}. */ @Deprecated @Override public Date getCreationDate() { return DateUtils.toDate(creationDate); } /** * @since 5.2 */ @Override public Instant getCreationInstant() { return creationDate; } /** * @return true if this Cookie has been marked as {@code httpOnly}, false otherwise * @see #setHttpOnly(boolean) * @since 5.2 */ @Override public boolean isHttpOnly() { return httpOnly; } /** * @since 4.4 * @deprecated Use {@link #setCreationDate(Instant)} */ @Deprecated public void setCreationDate(final Date creationDate) { this.creationDate = DateUtils.toInstant(creationDate); } /** * @since 5.2 */ public void setCreationDate(final Instant creationDate) { this.creationDate = creationDate; } public void setAttribute(final String name, final String value) { this.attribs.put(name, value); } @Override public String getAttribute(final String name) { return this.attribs.get(name); } @Override public boolean containsAttribute(final String name) { return this.attribs.containsKey(name); } /** * @since 4.4 */ public boolean removeAttribute(final String name) { return this.attribs.remove(name) != null; } @Override public Object clone() throws CloneNotSupportedException { final BasicClientCookie clone = (BasicClientCookie) super.clone(); clone.attribs = new HashMap<>(this.attribs); return clone; } @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("[name: "); buffer.append(this.name); buffer.append("; "); buffer.append("value: "); buffer.append(this.value); buffer.append("; "); buffer.append("domain: "); buffer.append(this.cookieDomain); buffer.append("; "); buffer.append("path: "); buffer.append(this.cookiePath); buffer.append("; "); buffer.append("expiry: "); buffer.append(this.cookieExpiryDate); buffer.append("]"); return buffer.toString(); } // ----------------------------------------------------- Instance Variables /** Cookie name */ private final String name; /** Cookie attributes as specified by the origin server */ private Map attribs; /** Cookie value */ private String value; /** Domain attribute. */ private String cookieDomain; /** Expiration {@link Instant}. */ private Instant cookieExpiryDate; /** Path attribute. */ private String cookiePath; /** My secure flag. */ private boolean isSecure; private Instant creationDate; /** The {@code httpOnly} flag. */ private boolean httpOnly; } BasicDomainHandler.java000066400000000000000000000124421434266521000373240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.util.Locale; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookieRestrictionViolationException; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Cookie {@code domain} attribute handler. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicDomainHandler implements CommonCookieAttributeHandler { /** * Singleton instance. * * @since 5.2 */ public static final BasicDomainHandler INSTANCE = new BasicDomainHandler(); public BasicDomainHandler() { super(); } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); if (TextUtils.isBlank(value)) { throw new MalformedCookieException("Blank or null value for domain attribute"); } // Ignore domain attributes ending with '.' per RFC 6265, 4.1.2.3 if (value.endsWith(".")) { return; } String domain = value; if (domain.startsWith(".")) { domain = domain.substring(1); } domain = domain.toLowerCase(Locale.ROOT); cookie.setDomain(domain); } @Override public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); // Validate the cookies domain attribute. NOTE: Domains without // any dots are allowed to support hosts on private LANs that don't // have DNS names. Since they have no dots, to domain-match the // request-host and domain must be identical for the cookie to sent // back to the origin-server. final String host = origin.getHost(); final String domain = cookie.getDomain(); if (domain == null) { throw new CookieRestrictionViolationException("Cookie 'domain' may not be null"); } if (!host.equals(domain) && !domainMatch(domain, host)) { throw new CookieRestrictionViolationException( "Illegal 'domain' attribute \"" + domain + "\". Domain of origin: \"" + host + "\""); } } static boolean domainMatch(final String domain, final String host) { if (InetAddressUtils.isIPv4Address(host) || InetAddressUtils.isIPv6Address(host)) { return false; } final String normalizedDomain = domain.startsWith(".") ? domain.substring(1) : domain; if (host.endsWith(normalizedDomain)) { final int prefix = host.length() - normalizedDomain.length(); // Either a full match or a prefix ending with a '.' if (prefix == 0) { return true; } return prefix > 1 && host.charAt(prefix - 1) == '.'; } return false; } @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); final String host = origin.getHost(); String domain = cookie.getDomain(); if (domain == null) { return false; } if (domain.startsWith(".")) { domain = domain.substring(1); } domain = domain.toLowerCase(Locale.ROOT); if (host.equals(domain)) { return true; } if ((cookie.containsAttribute(Cookie.DOMAIN_ATTR))) { return domainMatch(domain, host); } return false; } @Override public String getAttributeName() { return Cookie.DOMAIN_ATTR; } } BasicExpiresHandler.java000066400000000000000000000067221434266521000375400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Cookie {@code expires} attribute handler. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicExpiresHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler { /** Valid date patterns */ private final DateTimeFormatter[] datePatterns; /** * @since 5.2 */ public BasicExpiresHandler(final DateTimeFormatter... datePatterns) { this.datePatterns = datePatterns; } /** * @deprecated Use {@link #BasicExpiresHandler(DateTimeFormatter...)} */ @Deprecated public BasicExpiresHandler(final String[] datePatterns) { Args.notNull(datePatterns, "Array of date patterns"); this.datePatterns = new DateTimeFormatter[datePatterns.length]; for (int i = 0; i < datePatterns.length; i++) { this.datePatterns[i] = new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(datePatterns[i]) .toFormatter(); } } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); if (value == null) { throw new MalformedCookieException("Missing value for 'expires' attribute"); } final Instant expiry = DateUtils.parseDate(value, this.datePatterns); if (expiry == null) { throw new MalformedCookieException("Invalid 'expires' attribute: " + value); } cookie.setExpiryDate(expiry); } @Override public String getAttributeName() { return Cookie.EXPIRES_ATTR; } } BasicHttpOnlyHandler.java000066400000000000000000000051121434266521000376720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Cookie {@code HttpOnly} attribute handler. * * @since 5.2 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicHttpOnlyHandler implements CommonCookieAttributeHandler { /** * Singleton instance. * * @since 5.2 */ public static final BasicHttpOnlyHandler INSTANCE = new BasicHttpOnlyHandler(); public BasicHttpOnlyHandler() { super(); } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); cookie.setHttpOnly(true); } @Override public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { } @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { return true; } @Override public String getAttributeName() { return Cookie.HTTP_ONLY_ATTR; } }BasicMaxAgeHandler.java000066400000000000000000000055741434266521000372670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.time.Instant; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Cookie {@code max-age} attribute handler. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicMaxAgeHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler { /** * Singleton instance. * * @since 5.2 */ public static final BasicMaxAgeHandler INSTANCE = new BasicMaxAgeHandler(); public BasicMaxAgeHandler() { super(); } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); if (value == null) { throw new MalformedCookieException("Missing value for 'max-age' attribute"); } final int age; try { age = Integer.parseInt(value); } catch (final NumberFormatException e) { throw new MalformedCookieException ("Invalid 'max-age' attribute: " + value); } if (age < 0) { throw new MalformedCookieException ("Negative 'max-age' attribute: " + value); } cookie.setExpiryDate(Instant.now().plusSeconds(age)); } @Override public String getAttributeName() { return Cookie.MAX_AGE_ATTR; } } BasicPathHandler.java000066400000000000000000000070501434266521000370100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Cookie {@code path} attribute handler. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicPathHandler implements CommonCookieAttributeHandler { /** * Singleton instance. * * @since 5.2 */ public static final BasicPathHandler INSTANCE = new BasicPathHandler(); public BasicPathHandler() { super(); } @Override public void parse( final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); cookie.setPath(!TextUtils.isBlank(value) ? value : "/"); } @Override public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { } static boolean pathMatch(final String uriPath, final String cookiePath) { String normalizedCookiePath = cookiePath; if (normalizedCookiePath == null) { normalizedCookiePath = "/"; } if (normalizedCookiePath.length() > 1 && normalizedCookiePath.endsWith("/")) { normalizedCookiePath = normalizedCookiePath.substring(0, normalizedCookiePath.length() - 1); } if (uriPath.startsWith(normalizedCookiePath)) { if (normalizedCookiePath.equals("/")) { return true; } if (uriPath.length() == normalizedCookiePath.length()) { return true; } return uriPath.charAt(normalizedCookiePath.length()) == '/'; } return false; } @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); return pathMatch(origin.getPath(), cookie.getPath()); } @Override public String getAttributeName() { return Cookie.PATH_ATTR; } } BasicSecureHandler.java000066400000000000000000000051351434266521000373440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Cookie {@code secure} attribute handler. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicSecureHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler { /** * Singleton instance. * * @since 5.2 */ public static final BasicSecureHandler INSTANCE = new BasicSecureHandler(); public BasicSecureHandler() { super(); } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); cookie.setSecure(true); } @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); return !cookie.isSecure() || origin.isSecure(); } @Override public String getAttributeName() { return Cookie.SECURE_ATTR; } } CookieSpecBase.java000066400000000000000000000120021434266521000364640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieAttributeHandler; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.util.Args; /** * Cookie management functions shared by all specification. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class CookieSpecBase extends AbstractCookieSpec { public CookieSpecBase() { super(); } /** * @since 4.4 */ protected CookieSpecBase(final HashMap map) { super(map); } /** * @since 4.4 */ protected CookieSpecBase(final CommonCookieAttributeHandler... handlers) { super(handlers); } protected static String getDefaultPath(final CookieOrigin origin) { String defaultPath = origin.getPath(); int lastSlashIndex = defaultPath.lastIndexOf('/'); if (lastSlashIndex >= 0) { if (lastSlashIndex == 0) { //Do not remove the very first slash lastSlashIndex = 1; } defaultPath = defaultPath.substring(0, lastSlashIndex); } return defaultPath; } protected static String getDefaultDomain(final CookieOrigin origin) { return origin.getHost(); } protected List parse(final HeaderElement[] elems, final CookieOrigin origin) throws MalformedCookieException { final List cookies = new ArrayList<>(elems.length); for (final HeaderElement headerelement : elems) { final String name = headerelement.getName(); final String value = headerelement.getValue(); if (name == null || name.isEmpty()) { throw new MalformedCookieException("Cookie name may not be empty"); } final BasicClientCookie cookie = new BasicClientCookie(name, value); cookie.setPath(getDefaultPath(origin)); cookie.setDomain(getDefaultDomain(origin)); // cycle through the parameters final NameValuePair[] attribs = headerelement.getParameters(); for (int j = attribs.length - 1; j >= 0; j--) { final NameValuePair attrib = attribs[j]; final String s = attrib.getName().toLowerCase(Locale.ROOT); cookie.setAttribute(s, attrib.getValue()); final CookieAttributeHandler handler = findAttribHandler(s); if (handler != null) { handler.parse(cookie, attrib.getValue()); } } cookies.add(cookie); } return cookies; } @Override public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); for (final CookieAttributeHandler handler: getAttribHandlers()) { handler.validate(cookie, origin); } } @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); for (final CookieAttributeHandler handler: getAttribHandlers()) { if (!handler.match(cookie, origin)) { return false; } } return true; } } IgnoreCookieSpecFactory.java000066400000000000000000000041511434266521000403730ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link CookieSpecFactory} implementation that ignores all cookies. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.SAFE) public class IgnoreCookieSpecFactory implements CookieSpecFactory { private volatile CookieSpec cookieSpec; public IgnoreCookieSpecFactory() { super(); } @Override public CookieSpec create(final HttpContext context) { if (cookieSpec == null) { synchronized (this) { if (cookieSpec == null) { this.cookieSpec = IgnoreSpecSpec.INSTANCE; } } } return this.cookieSpec; } } IgnoreSpecSpec.java000066400000000000000000000044551434266521000365330ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.util.Collections; import java.util.List; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.Header; /** * CookieSpec that ignores all cookies * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public class IgnoreSpecSpec extends CookieSpecBase { /** * Singleton instance. * * @since 5.2 */ public static final IgnoreSpecSpec INSTANCE = new IgnoreSpecSpec(); @Override public List parse(final Header header, final CookieOrigin origin) throws MalformedCookieException { return Collections.emptyList(); } @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { return false; } @Override public List

formatCookies(final List cookies) { return Collections.emptyList(); } } LaxExpiresHandler.java000066400000000000000000000205051434266521000372360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.time.Instant; import java.time.Month; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.BitSet; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; import org.apache.hc.core5.util.Tokenizer; /** * Cookie {@code expires} attribute handler conformant to the more relaxed interpretation * of HTTP state management. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler { /** * Singleton instance. * * @since 5.2 */ public static final LaxExpiresHandler INSTANCE = new LaxExpiresHandler(); private static final BitSet DELIMS; static { final BitSet bitSet = new BitSet(); bitSet.set(0x9); for (int b = 0x20; b <= 0x2f; b++) { bitSet.set(b); } for (int b = 0x3b; b <= 0x40; b++) { bitSet.set(b); } for (int b = 0x5b; b <= 0x60; b++) { bitSet.set(b); } for (int b = 0x7b; b <= 0x7e; b++) { bitSet.set(b); } DELIMS = bitSet; } private static final Map MONTHS; static { final ConcurrentHashMap map = new ConcurrentHashMap<>(12); map.put("jan", Month.JANUARY); map.put("feb", Month.FEBRUARY); map.put("mar", Month.MARCH); map.put("apr", Month.APRIL); map.put("may", Month.MAY); map.put("jun", Month.JUNE); map.put("jul", Month.JULY); map.put("aug", Month.AUGUST); map.put("sep", Month.SEPTEMBER); map.put("oct", Month.OCTOBER); map.put("nov", Month.NOVEMBER); map.put("dec", Month.DECEMBER); MONTHS = map; } private final static Pattern TIME_PATTERN = Pattern.compile( "^([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})([^0-9].*)?$"); private final static Pattern DAY_OF_MONTH_PATTERN = Pattern.compile( "^([0-9]{1,2})([^0-9].*)?$"); private final static Pattern MONTH_PATTERN = Pattern.compile( "^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)(.*)?$", Pattern.CASE_INSENSITIVE); private final static Pattern YEAR_PATTERN = Pattern.compile( "^([0-9]{2,4})([^0-9].*)?$"); public LaxExpiresHandler() { super(); } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); if (TextUtils.isBlank(value)) { return; } final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, value.length()); final StringBuilder content = new StringBuilder(); int second = 0, minute = 0, hour = 0, day = 0, year = 0; Month month = Month.JANUARY; boolean foundTime = false, foundDayOfMonth = false, foundMonth = false, foundYear = false; try { while (!cursor.atEnd()) { skipDelims(value, cursor); content.setLength(0); copyContent(value, cursor, content); if (content.length() == 0) { break; } if (!foundTime) { final Matcher matcher = TIME_PATTERN.matcher(content); if (matcher.matches()) { foundTime = true; hour = Integer.parseInt(matcher.group(1)); minute = Integer.parseInt(matcher.group(2)); second =Integer.parseInt(matcher.group(3)); continue; } } if (!foundDayOfMonth) { final Matcher matcher = DAY_OF_MONTH_PATTERN.matcher(content); if (matcher.matches()) { foundDayOfMonth = true; day = Integer.parseInt(matcher.group(1)); continue; } } if (!foundMonth) { final Matcher matcher = MONTH_PATTERN.matcher(content); if (matcher.matches()) { foundMonth = true; month = MONTHS.get(matcher.group(1).toLowerCase(Locale.ROOT)); continue; } } if (!foundYear) { final Matcher matcher = YEAR_PATTERN.matcher(content); if (matcher.matches()) { foundYear = true; year = Integer.parseInt(matcher.group(1)); continue; } } } } catch (final NumberFormatException ignore) { throw new MalformedCookieException("Invalid 'expires' attribute: " + value); } if (!foundTime || !foundDayOfMonth || !foundMonth || !foundYear) { throw new MalformedCookieException("Invalid 'expires' attribute: " + value); } if (year >= 70 && year <= 99) { year = 1900 + year; } if (year >= 0 && year <= 69) { year = 2000 + year; } if (day < 1 || day > 31 || year < 1601 || hour > 23 || minute > 59 || second > 59) { throw new MalformedCookieException("Invalid 'expires' attribute: " + value); } final Instant expiryDate = ZonedDateTime.of(year, month.getValue(), day, hour, minute, second, 0, ZoneId.of("UTC")).toInstant(); cookie.setExpiryDate(expiryDate); } private void skipDelims(final CharSequence buf, final Tokenizer.Cursor cursor) { int pos = cursor.getPos(); final int indexFrom = cursor.getPos(); final int indexTo = cursor.getUpperBound(); for (int i = indexFrom; i < indexTo; i++) { final char current = buf.charAt(i); if (DELIMS.get(current)) { pos++; } else { break; } } cursor.updatePos(pos); } private void copyContent(final CharSequence buf, final Tokenizer.Cursor cursor, final StringBuilder dst) { int pos = cursor.getPos(); final int indexFrom = cursor.getPos(); final int indexTo = cursor.getUpperBound(); for (int i = indexFrom; i < indexTo; i++) { final char current = buf.charAt(i); if (DELIMS.get(current)) { break; } pos++; dst.append(current); } cursor.updatePos(pos); } @Override public String getAttributeName() { return Cookie.EXPIRES_ATTR; } } LaxMaxAgeHandler.java000066400000000000000000000060671434266521000367700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.time.Instant; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * Cookie {@code max-age} attribute handler conformant to the more relaxed interpretation * of HTTP state management. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public class LaxMaxAgeHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler { /** * Singleton instance. * * @since 5.2 */ public static final LaxMaxAgeHandler INSTANCE = new LaxMaxAgeHandler(); private final static Pattern MAX_AGE_PATTERN = Pattern.compile("^\\-?[0-9]+$"); public LaxMaxAgeHandler() { super(); } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); if (TextUtils.isBlank(value)) { return; } final Matcher matcher = MAX_AGE_PATTERN.matcher(value); if (matcher.matches()) { final int age; try { age = Integer.parseInt(value); } catch (final NumberFormatException e) { return; } final Instant expiryDate = age >= 0 ? Instant.now().plusSeconds(age) : Instant.EPOCH; cookie.setExpiryDate(expiryDate); } } @Override public String getAttributeName() { return Cookie.MAX_AGE_ATTR; } } PublicSuffixDomainFilter.java000066400000000000000000000124561434266521000405630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.cookie.SetCookie; import org.apache.hc.client5.http.psl.PublicSuffixList; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Wraps a {@link org.apache.hc.client5.http.cookie.CookieAttributeHandler} and leverages * its match method to never match a suffix from a black list. May be used to provide * additional security for cross-site attack types by preventing cookies from apparent * domains that are not publicly available. * * @see PublicSuffixList * @see PublicSuffixMatcher * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public class PublicSuffixDomainFilter implements CommonCookieAttributeHandler { private final CommonCookieAttributeHandler handler; private final PublicSuffixMatcher publicSuffixMatcher; private final Map localDomainMap; private static Map createLocalDomainMap() { final ConcurrentHashMap map = new ConcurrentHashMap<>(); map.put(".localhost.", Boolean.TRUE); // RFC 6761 map.put(".test.", Boolean.TRUE); // RFC 6761 map.put(".local.", Boolean.TRUE); // RFC 6762 map.put(".local", Boolean.TRUE); map.put(".localdomain", Boolean.TRUE); return map; } public PublicSuffixDomainFilter( final CommonCookieAttributeHandler handler, final PublicSuffixMatcher publicSuffixMatcher) { this.handler = Args.notNull(handler, "Cookie handler"); this.publicSuffixMatcher = Args.notNull(publicSuffixMatcher, "Public suffix matcher"); this.localDomainMap = createLocalDomainMap(); } public PublicSuffixDomainFilter( final CommonCookieAttributeHandler handler, final PublicSuffixList suffixList) { Args.notNull(handler, "Cookie handler"); Args.notNull(suffixList, "Public suffix list"); this.handler = handler; this.publicSuffixMatcher = new PublicSuffixMatcher(suffixList.getRules(), suffixList.getExceptions()); this.localDomainMap = createLocalDomainMap(); } /** * Never matches if the cookie's domain is from the blacklist. */ @Override public boolean match(final Cookie cookie, final CookieOrigin origin) { final String host = cookie.getDomain(); if (host == null) { return false; } final int i = host.indexOf('.'); if (i >= 0) { final String domain = host.substring(i); if (!this.localDomainMap.containsKey(domain)) { if (this.publicSuffixMatcher.matches(host)) { return false; } } } else { if (!host.equalsIgnoreCase(origin.getHost())) { if (this.publicSuffixMatcher.matches(host)) { return false; } } } return handler.match(cookie, origin); } @Override public void parse(final SetCookie cookie, final String value) throws MalformedCookieException { handler.parse(cookie, value); } @Override public void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { handler.validate(cookie, origin); } @Override public String getAttributeName() { return handler.getAttributeName(); } public static CommonCookieAttributeHandler decorate( final CommonCookieAttributeHandler handler, final PublicSuffixMatcher publicSuffixMatcher) { Args.notNull(handler, "Cookie attribute handler"); return publicSuffixMatcher != null ? new PublicSuffixDomainFilter(handler, publicSuffixMatcher) : handler; } } RFC6265CookieSpec.java000066400000000000000000000251001434266521000365520ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.time.Instant; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieAttributeHandler; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookiePriorityComparator; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.FormattedHeader; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.BufferedHeader; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.Tokenizer; /** * Cookie management functions shared by RFC C6265 compliant specification. * * @since 4.5 */ @Contract(threading = ThreadingBehavior.SAFE) public class RFC6265CookieSpec implements CookieSpec { private final static char PARAM_DELIMITER = ';'; private final static char COMMA_CHAR = ','; private final static char EQUAL_CHAR = '='; private final static char DQUOTE_CHAR = '"'; private final static char ESCAPE_CHAR = '\\'; // IMPORTANT! // These private static variables must be treated as immutable and never exposed outside this class private static final BitSet TOKEN_DELIMS = Tokenizer.INIT_BITSET(EQUAL_CHAR, PARAM_DELIMITER); private static final BitSet VALUE_DELIMS = Tokenizer.INIT_BITSET(PARAM_DELIMITER); private static final BitSet SPECIAL_CHARS = Tokenizer.INIT_BITSET(' ', DQUOTE_CHAR, COMMA_CHAR, PARAM_DELIMITER, ESCAPE_CHAR); private final CookieAttributeHandler[] attribHandlers; private final Map attribHandlerMap; private final Tokenizer tokenParser; protected RFC6265CookieSpec(final CommonCookieAttributeHandler... handlers) { super(); this.attribHandlers = handlers.clone(); this.attribHandlerMap = new ConcurrentHashMap<>(handlers.length); for (final CommonCookieAttributeHandler handler: handlers) { this.attribHandlerMap.put(handler.getAttributeName().toLowerCase(Locale.ROOT), handler); } this.tokenParser = Tokenizer.INSTANCE; } static String getDefaultPath(final CookieOrigin origin) { String defaultPath = origin.getPath(); int lastSlashIndex = defaultPath.lastIndexOf('/'); if (lastSlashIndex >= 0) { if (lastSlashIndex == 0) { //Do not remove the very first slash lastSlashIndex = 1; } defaultPath = defaultPath.substring(0, lastSlashIndex); } return defaultPath; } static String getDefaultDomain(final CookieOrigin origin) { return origin.getHost(); } @Override public final List parse(final Header header, final CookieOrigin origin) throws MalformedCookieException { Args.notNull(header, "Header"); Args.notNull(origin, "Cookie origin"); if (!header.getName().equalsIgnoreCase("Set-Cookie")) { throw new MalformedCookieException("Unrecognized cookie header: '" + header + "'"); } final CharArrayBuffer buffer; final Tokenizer.Cursor cursor; if (header instanceof FormattedHeader) { buffer = ((FormattedHeader) header).getBuffer(); cursor = new Tokenizer.Cursor(((FormattedHeader) header).getValuePos(), buffer.length()); } else { final String s = header.getValue(); if (s == null) { throw new MalformedCookieException("Header value is null"); } buffer = new CharArrayBuffer(s.length()); buffer.append(s); cursor = new Tokenizer.Cursor(0, buffer.length()); } final String name = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS); if (name.isEmpty()) { return Collections.emptyList(); } if (cursor.atEnd()) { return Collections.emptyList(); } final int valueDelim = buffer.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (valueDelim != '=') { throw new MalformedCookieException("Cookie value is invalid: '" + header + "'"); } final String value = tokenParser.parseValue(buffer, cursor, VALUE_DELIMS); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } final BasicClientCookie cookie = new BasicClientCookie(name, value); cookie.setPath(getDefaultPath(origin)); cookie.setDomain(getDefaultDomain(origin)); cookie.setCreationDate(Instant.now()); final Map attribMap = new LinkedHashMap<>(); while (!cursor.atEnd()) { final String paramName = tokenParser.parseToken(buffer, cursor, TOKEN_DELIMS) .toLowerCase(Locale.ROOT); String paramValue = null; if (!cursor.atEnd()) { final int paramDelim = buffer.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (paramDelim == EQUAL_CHAR) { paramValue = tokenParser.parseToken(buffer, cursor, VALUE_DELIMS); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } } } cookie.setAttribute(paramName, paramValue); attribMap.put(paramName, paramValue); } // Ignore 'Expires' if 'Max-Age' is present if (attribMap.containsKey(Cookie.MAX_AGE_ATTR)) { attribMap.remove(Cookie.EXPIRES_ATTR); } for (final Map.Entry entry: attribMap.entrySet()) { final String paramName = entry.getKey(); final String paramValue = entry.getValue(); final CookieAttributeHandler handler = this.attribHandlerMap.get(paramName); if (handler != null) { handler.parse(cookie, paramValue); } } return Collections.singletonList(cookie); } @Override public final void validate(final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); for (final CookieAttributeHandler handler: this.attribHandlers) { handler.validate(cookie, origin); } } @Override public final boolean match(final Cookie cookie, final CookieOrigin origin) { Args.notNull(cookie, "Cookie"); Args.notNull(origin, "Cookie origin"); for (final CookieAttributeHandler handler: this.attribHandlers) { if (!handler.match(cookie, origin)) { return false; } } return true; } @Override public List
formatCookies(final List cookies) { Args.notEmpty(cookies, "List of cookies"); final List sortedCookies; if (cookies.size() > 1) { // Create a mutable copy and sort the copy. sortedCookies = new ArrayList<>(cookies); sortedCookies.sort(CookiePriorityComparator.INSTANCE); } else { sortedCookies = cookies; } final CharArrayBuffer buffer = new CharArrayBuffer(20 * sortedCookies.size()); buffer.append("Cookie"); buffer.append(": "); for (int n = 0; n < sortedCookies.size(); n++) { final Cookie cookie = sortedCookies.get(n); if (n > 0) { buffer.append(PARAM_DELIMITER); buffer.append(' '); } buffer.append(cookie.getName()); final String s = cookie.getValue(); if (s != null) { buffer.append(EQUAL_CHAR); if (containsSpecialChar(s)) { buffer.append(DQUOTE_CHAR); for (int i = 0; i < s.length(); i++) { final char ch = s.charAt(i); if (ch == DQUOTE_CHAR || ch == ESCAPE_CHAR) { buffer.append(ESCAPE_CHAR); } buffer.append(ch); } buffer.append(DQUOTE_CHAR); } else { buffer.append(s); } } } final List
headers = new ArrayList<>(1); try { headers.add(new BufferedHeader(buffer)); } catch (final ParseException ignore) { // should never happen } return headers; } boolean containsSpecialChar(final CharSequence s) { return containsChars(s, SPECIAL_CHARS); } boolean containsChars(final CharSequence s, final BitSet chars) { for (int i = 0; i < s.length(); i++) { final char ch = s.charAt(i); if (chars.get(ch)) { return true; } } return false; } } RFC6265CookieSpecBase.java000066400000000000000000000027011434266521000373470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; class RFC6265CookieSpecBase extends RFC6265CookieSpec { RFC6265CookieSpecBase(final CommonCookieAttributeHandler... handlers) { super(handlers); } } RFC6265CookieSpecFactory.java000066400000000000000000000127541434266521000401150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link CookieSpecFactory} implementation that provides an instance of * RFC 6265 conformant cookie policy. The instance returned by this factory * can be shared by multiple threads. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.SAFE) public class RFC6265CookieSpecFactory implements CookieSpecFactory { public enum CompatibilityLevel { STRICT, RELAXED, IE_MEDIUM_SECURITY } private final CompatibilityLevel compatibilityLevel; private final PublicSuffixMatcher publicSuffixMatcher; private volatile CookieSpec cookieSpec; public RFC6265CookieSpecFactory( final CompatibilityLevel compatibilityLevel, final PublicSuffixMatcher publicSuffixMatcher) { super(); this.compatibilityLevel = compatibilityLevel != null ? compatibilityLevel : CompatibilityLevel.RELAXED; this.publicSuffixMatcher = publicSuffixMatcher; } public RFC6265CookieSpecFactory(final PublicSuffixMatcher publicSuffixMatcher) { this(CompatibilityLevel.RELAXED, publicSuffixMatcher); } public RFC6265CookieSpecFactory() { this(CompatibilityLevel.RELAXED, null); } @Override public CookieSpec create(final HttpContext context) { if (cookieSpec == null) { synchronized (this) { if (cookieSpec == null) { switch (this.compatibilityLevel) { case STRICT: this.cookieSpec = new RFC6265StrictSpec( BasicPathHandler.INSTANCE, PublicSuffixDomainFilter.decorate( BasicDomainHandler.INSTANCE, this.publicSuffixMatcher), BasicMaxAgeHandler.INSTANCE, BasicSecureHandler.INSTANCE, BasicHttpOnlyHandler.INSTANCE, new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS)); break; case IE_MEDIUM_SECURITY: this.cookieSpec = new RFC6265LaxSpec( new BasicPathHandler() { @Override public void validate( final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { // No validation } }, PublicSuffixDomainFilter.decorate( BasicDomainHandler.INSTANCE, this.publicSuffixMatcher), BasicMaxAgeHandler.INSTANCE, BasicSecureHandler.INSTANCE, BasicHttpOnlyHandler.INSTANCE, new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS)); break; default: this.cookieSpec = new RFC6265LaxSpec( BasicPathHandler.INSTANCE, PublicSuffixDomainFilter.decorate( BasicDomainHandler.INSTANCE, this.publicSuffixMatcher), LaxMaxAgeHandler.INSTANCE, BasicSecureHandler.INSTANCE, LaxExpiresHandler.INSTANCE); } } } } return this.cookieSpec; } } RFC6265LaxSpec.java000066400000000000000000000044471434266521000361000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Standard {@link org.apache.hc.client5.http.cookie.CookieSpec} implementation that enforces * a more relaxed interpretation of the HTTP state management specification (RFC 6265, section 5) * for interoperability with existing servers that do not conform to the well behaved profile * (RFC 6265, section 4). * * @since 4.4 */ @Contract(threading = ThreadingBehavior.SAFE) public class RFC6265LaxSpec extends RFC6265CookieSpecBase { public RFC6265LaxSpec() { super(BasicPathHandler.INSTANCE, BasicDomainHandler.INSTANCE, LaxMaxAgeHandler.INSTANCE, BasicSecureHandler.INSTANCE, BasicHttpOnlyHandler.INSTANCE, LaxExpiresHandler.INSTANCE); } RFC6265LaxSpec(final CommonCookieAttributeHandler... handlers) { super(handlers); } @Override public String toString() { return "rfc6265-lax"; } } RFC6265StrictSpec.java000066400000000000000000000044411434266521000366160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Standard {@link org.apache.hc.client5.http.cookie.CookieSpec} implementation * that enforces syntax and semantics of the well-behaved profile of the HTTP * state management specification (RFC 6265, section 4). * * @since 4.4 */ @Contract(threading = ThreadingBehavior.SAFE) public class RFC6265StrictSpec extends RFC6265CookieSpecBase { public RFC6265StrictSpec() { super(BasicPathHandler.INSTANCE, BasicDomainHandler.INSTANCE, BasicMaxAgeHandler.INSTANCE, BasicSecureHandler.INSTANCE, BasicHttpOnlyHandler.INSTANCE, new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS)); } RFC6265StrictSpec(final CommonCookieAttributeHandler... handlers) { super(handlers); } @Override public String toString() { return "rfc6265-strict"; } } package-info.java000066400000000000000000000024211434266521000361750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Standard and common HTTP cookie management policies. */ package org.apache.hc.client5.http.impl.cookie; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/000077500000000000000000000000001434266521000322245ustar00rootroot00000000000000BasicHttpClientConnectionManager.java000066400000000000000000000516061434266521000413530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.time.Instant; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.ConnectionShutdownException; import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.io.HttpClientConnectionOperator; import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Deadline; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A connection manager for a single connection. This connection manager maintains only one active * connection. Even though this class is fully thread-safe it ought to be used by one execution * thread only, as only one thread a time can lease the connection at a time. *

* This connection manager will make an effort to reuse the connection for subsequent requests * with the same {@link HttpRoute route}. It will, however, close the existing connection and * open it for the given route, if the route of the persistent connection does not match that * of the connection request. If the connection has been already been allocated * {@link IllegalStateException} is thrown. *

*

* This connection manager implementation should be used inside an EJB container instead of * {@link PoolingHttpClientConnectionManager}. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE) public class BasicHttpClientConnectionManager implements HttpClientConnectionManager { private static final Logger LOG = LoggerFactory.getLogger(BasicHttpClientConnectionManager.class); private static final AtomicLong COUNT = new AtomicLong(0); private final HttpClientConnectionOperator connectionOperator; private final HttpConnectionFactory connFactory; private final String id; private ManagedHttpClientConnection conn; private HttpRoute route; private Object state; private long created; private long updated; private long expiry; private boolean leased; private SocketConfig socketConfig; private ConnectionConfig connectionConfig; private TlsConfig tlsConfig; private final AtomicBoolean closed; private static Registry getDefaultRegistry() { return RegistryBuilder.create() .register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory()) .register(URIScheme.HTTPS.id, SSLConnectionSocketFactory.getSocketFactory()) .build(); } public BasicHttpClientConnectionManager( final Lookup socketFactoryRegistry, final HttpConnectionFactory connFactory, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver) { this(new DefaultHttpClientConnectionOperator( socketFactoryRegistry, schemePortResolver, dnsResolver), connFactory); } /** * @since 4.4 */ public BasicHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, final HttpConnectionFactory connFactory) { super(); this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator"); this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; this.id = String.format("ep-%010d", COUNT.getAndIncrement()); this.expiry = Long.MAX_VALUE; this.socketConfig = SocketConfig.DEFAULT; this.connectionConfig = ConnectionConfig.DEFAULT; this.tlsConfig = TlsConfig.DEFAULT; this.closed = new AtomicBoolean(false); } public BasicHttpClientConnectionManager( final Lookup socketFactoryRegistry, final HttpConnectionFactory connFactory) { this(socketFactoryRegistry, connFactory, null, null); } public BasicHttpClientConnectionManager( final Lookup socketFactoryRegistry) { this(socketFactoryRegistry, null, null, null); } public BasicHttpClientConnectionManager() { this(getDefaultRegistry(), null, null, null); } @Override public void close() { close(CloseMode.GRACEFUL); } @Override public void close(final CloseMode closeMode) { if (this.closed.compareAndSet(false, true)) { closeConnection(closeMode); } } HttpRoute getRoute() { return route; } Object getState() { return state; } public synchronized SocketConfig getSocketConfig() { return socketConfig; } public synchronized void setSocketConfig(final SocketConfig socketConfig) { this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; } /** * @since 5.2 */ public synchronized ConnectionConfig getConnectionConfig() { return connectionConfig; } /** * @since 5.2 */ public synchronized void setConnectionConfig(final ConnectionConfig connectionConfig) { this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT; } /** * @since 5.2 */ public synchronized TlsConfig getTlsConfig() { return tlsConfig; } /** * @since 5.2 */ public synchronized void setTlsConfig(final TlsConfig tlsConfig) { this.tlsConfig = tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT; } public LeaseRequest lease(final String id, final HttpRoute route, final Object state) { return lease(id, route, Timeout.DISABLED, state); } @Override public LeaseRequest lease(final String id, final HttpRoute route, final Timeout requestTimeout, final Object state) { return new LeaseRequest() { @Override public ConnectionEndpoint get( final Timeout timeout) throws InterruptedException, ExecutionException, TimeoutException { try { return new InternalConnectionEndpoint(route, getConnection(route, state)); } catch (final IOException ex) { throw new ExecutionException(ex.getMessage(), ex); } } @Override public boolean cancel() { return false; } }; } private synchronized void closeConnection(final CloseMode closeMode) { if (this.conn != null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Closing connection {}", id, closeMode); } this.conn.close(closeMode); this.conn = null; } } private void checkExpiry() { if (this.conn != null && System.currentTimeMillis() >= this.expiry) { if (LOG.isDebugEnabled()) { LOG.debug("{} Connection expired @ {}", id, Instant.ofEpochMilli(this.expiry)); } closeConnection(CloseMode.GRACEFUL); } } private void validate() { if (this.conn != null) { final TimeValue timeToLive = connectionConfig.getTimeToLive(); if (TimeValue.isNonNegative(timeToLive)) { final Deadline deadline = Deadline.calculate(created, timeToLive); if (deadline.isExpired()) { closeConnection(CloseMode.GRACEFUL); } } } if (this.conn != null) { final TimeValue timeValue = connectionConfig.getValidateAfterInactivity() != null ? connectionConfig.getValidateAfterInactivity() : TimeValue.ofSeconds(2); if (TimeValue.isNonNegative(timeValue)) { final Deadline deadline = Deadline.calculate(updated, timeValue); if (deadline.isExpired()) { boolean stale; try { stale = conn.isStale(); } catch (final IOException ignore) { stale = true; } if (stale) { if (LOG.isDebugEnabled()) { LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn)); } closeConnection(CloseMode.GRACEFUL); } } } } } synchronized ManagedHttpClientConnection getConnection(final HttpRoute route, final Object state) throws IOException { Asserts.check(!isClosed(), "Connection manager has been shut down"); if (LOG.isDebugEnabled()) { LOG.debug("{} Get connection for route {}", id, route); } Asserts.check(!this.leased, "Connection %s is still allocated", conn); if (!Objects.equals(this.route, route) || !Objects.equals(this.state, state)) { closeConnection(CloseMode.GRACEFUL); } this.route = route; this.state = state; checkExpiry(); validate(); if (this.conn == null) { this.conn = this.connFactory.createConnection(null); this.created = System.currentTimeMillis(); } else { this.conn.activate(); } this.leased = true; if (LOG.isDebugEnabled()) { LOG.debug("{} Using connection {}", id, conn); } return this.conn; } private InternalConnectionEndpoint cast(final ConnectionEndpoint endpoint) { if (endpoint instanceof InternalConnectionEndpoint) { return (InternalConnectionEndpoint) endpoint; } throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass()); } @Override public synchronized void release(final ConnectionEndpoint endpoint, final Object state, final TimeValue keepAlive) { Args.notNull(endpoint, "Managed endpoint"); final InternalConnectionEndpoint internalEndpoint = cast(endpoint); final ManagedHttpClientConnection conn = internalEndpoint.detach(); if (LOG.isDebugEnabled()) { LOG.debug("{} Releasing connection {}", id, conn); } if (isClosed()) { return; } try { if (keepAlive == null) { this.conn.close(CloseMode.GRACEFUL); } this.updated = System.currentTimeMillis(); if (!this.conn.isOpen() && !this.conn.isConsistent()) { this.route = null; this.conn = null; this.expiry = Long.MAX_VALUE; if (LOG.isDebugEnabled()) { LOG.debug("{} Connection is not kept alive", id); } } else { this.state = state; if (conn != null) { conn.passivate(); } if (TimeValue.isPositive(keepAlive)) { if (LOG.isDebugEnabled()) { LOG.debug("{} Connection can be kept alive for {}", id, keepAlive); } this.expiry = this.updated + keepAlive.toMilliseconds(); } else { if (LOG.isDebugEnabled()) { LOG.debug("{} Connection can be kept alive indefinitely", id); } this.expiry = Long.MAX_VALUE; } } } finally { this.leased = false; } } @Override public synchronized void connect(final ConnectionEndpoint endpoint, final TimeValue timeout, final HttpContext context) throws IOException { Args.notNull(endpoint, "Endpoint"); final InternalConnectionEndpoint internalEndpoint = cast(endpoint); if (internalEndpoint.isConnected()) { return; } final HttpRoute route = internalEndpoint.getRoute(); final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : connectionConfig.getConnectTimeout(); final ManagedHttpClientConnection connection = internalEndpoint.getConnection(); if (LOG.isDebugEnabled()) { LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout); } this.connectionOperator.connect( connection, host, route.getLocalSocketAddress(), connectTimeout, socketConfig, tlsConfig, context); if (LOG.isDebugEnabled()) { LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn)); } final Timeout socketTimeout = connectionConfig.getSocketTimeout(); if (socketTimeout != null) { connection.setSocketTimeout(socketTimeout); } } @Override public synchronized void upgrade( final ConnectionEndpoint endpoint, final HttpContext context) throws IOException { Args.notNull(endpoint, "Endpoint"); Args.notNull(route, "HTTP route"); final InternalConnectionEndpoint internalEndpoint = cast(endpoint); this.connectionOperator.upgrade( internalEndpoint.getConnection(), internalEndpoint.getRoute().getTargetHost(), tlsConfig, context); } public synchronized void closeExpired() { if (isClosed()) { return; } if (!this.leased) { checkExpiry(); } } public synchronized void closeIdle(final TimeValue idleTime) { Args.notNull(idleTime, "Idle time"); if (isClosed()) { return; } if (!this.leased) { long time = idleTime.toMilliseconds(); if (time < 0) { time = 0; } final long deadline = System.currentTimeMillis() - time; if (this.updated <= deadline) { closeConnection(CloseMode.GRACEFUL); } } } /** * @see #setValidateAfterInactivity(TimeValue) * * @since 5.1 * * @deprecated Use {@link #getConnectionConfig()} */ @Deprecated public TimeValue getValidateAfterInactivity() { return connectionConfig.getValidateAfterInactivity(); } /** * Defines period of inactivity after which persistent connections must * be re-validated prior to being {@link #lease(String, HttpRoute, Object)} leased} to the consumer. * Negative values passed to this method disable connection validation. This check helps * detect connections that have become stale (half-closed) while kept inactive in the pool. * * @since 5.1 * * @deprecated Use {@link #setConnectionConfig(ConnectionConfig)} */ @Deprecated public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) { this.connectionConfig = ConnectionConfig.custom() .setValidateAfterInactivity(validateAfterInactivity) .build(); } class InternalConnectionEndpoint extends ConnectionEndpoint { private final HttpRoute route; private final AtomicReference connRef; public InternalConnectionEndpoint(final HttpRoute route, final ManagedHttpClientConnection conn) { this.route = route; this.connRef = new AtomicReference<>(conn); } HttpRoute getRoute() { return route; } ManagedHttpClientConnection getConnection() { final ManagedHttpClientConnection conn = this.connRef.get(); if (conn == null) { throw new ConnectionShutdownException(); } return conn; } ManagedHttpClientConnection getValidatedConnection() { final ManagedHttpClientConnection conn = getConnection(); Asserts.check(conn.isOpen(), "Endpoint is not connected"); return conn; } ManagedHttpClientConnection detach() { return this.connRef.getAndSet(null); } @Override public boolean isConnected() { final ManagedHttpClientConnection conn = getConnection(); return conn != null && conn.isOpen(); } @Override public void close(final CloseMode closeMode) { final ManagedHttpClientConnection conn = detach(); if (conn != null) { conn.close(closeMode); } } @Override public void close() throws IOException { final ManagedHttpClientConnection conn = detach(); if (conn != null) { conn.close(); } } @Override public void setSocketTimeout(final Timeout timeout) { getValidatedConnection().setSocketTimeout(timeout); } @Override public ClassicHttpResponse execute( final String exchangeId, final ClassicHttpRequest request, final HttpRequestExecutor requestExecutor, final HttpContext context) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(requestExecutor, "Request executor"); if (LOG.isDebugEnabled()) { LOG.debug("{} Executing exchange {}", id, exchangeId); } return requestExecutor.execute(request, getValidatedConnection(), context); } } /** * Method that can be called to determine whether the connection manager has been shut down and * is closed or not. * * @return {@code true} if the connection manager has been shut down and is closed, otherwise * return {@code false}. */ boolean isClosed() { return this.closed.get(); } } DefaultHttpClientConnectionOperator.java000066400000000000000000000246741434266521000421440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.Arrays; import org.apache.hc.client5.http.ConnectExceptionSupport; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.UnsupportedSchemeException; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.io.HttpClientConnectionOperator; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default implementation of {@link HttpClientConnectionOperator} used as default in Http client, * when no instance provided by user to {@link BasicHttpClientConnectionManager} or {@link * PoolingHttpClientConnectionManager} constructor. * * @since 4.4 */ @Internal @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator { static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry"; private static final Logger LOG = LoggerFactory.getLogger(DefaultHttpClientConnectionOperator.class); private final Lookup socketFactoryRegistry; private final SchemePortResolver schemePortResolver; private final DnsResolver dnsResolver; public DefaultHttpClientConnectionOperator( final Lookup socketFactoryRegistry, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver) { super(); Args.notNull(socketFactoryRegistry, "Socket factory registry"); this.socketFactoryRegistry = socketFactoryRegistry; this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; this.dnsResolver = dnsResolver != null ? dnsResolver : SystemDefaultDnsResolver.INSTANCE; } @SuppressWarnings("unchecked") private Lookup getSocketFactoryRegistry(final HttpContext context) { Lookup reg = (Lookup) context.getAttribute( SOCKET_FACTORY_REGISTRY); if (reg == null) { reg = this.socketFactoryRegistry; } return reg; } @Override public void connect( final ManagedHttpClientConnection conn, final HttpHost host, final InetSocketAddress localAddress, final TimeValue connectTimeout, final SocketConfig socketConfig, final HttpContext context) throws IOException { final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null; connect(conn, host, localAddress, timeout, socketConfig, null, context); } @Override public void connect( final ManagedHttpClientConnection conn, final HttpHost host, final InetSocketAddress localAddress, final Timeout connectTimeout, final SocketConfig socketConfig, final Object attachment, final HttpContext context) throws IOException { Args.notNull(conn, "Connection"); Args.notNull(host, "Host"); Args.notNull(socketConfig, "Socket config"); Args.notNull(context, "Context"); final Lookup registry = getSocketFactoryRegistry(context); final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); if (sf == null) { throw new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported"); } final InetAddress[] remoteAddresses; if (host.getAddress() != null) { remoteAddresses = new InetAddress[] { host.getAddress() }; } else { if (LOG.isDebugEnabled()) { LOG.debug("{} resolving remote address", host.getHostName()); } remoteAddresses = this.dnsResolver.resolve(host.getHostName()); if (LOG.isDebugEnabled()) { LOG.debug("{} resolved to {}", host.getHostName(), Arrays.asList(remoteAddresses)); } } final Timeout soTimeout = socketConfig.getSoTimeout(); final int port = this.schemePortResolver.resolve(host); for (int i = 0; i < remoteAddresses.length; i++) { final InetAddress address = remoteAddresses[i]; final boolean last = i == remoteAddresses.length - 1; Socket sock = sf.createSocket(context); if (soTimeout != null) { sock.setSoTimeout(soTimeout.toMillisecondsIntBound()); } sock.setReuseAddress(socketConfig.isSoReuseAddress()); sock.setTcpNoDelay(socketConfig.isTcpNoDelay()); sock.setKeepAlive(socketConfig.isSoKeepAlive()); if (socketConfig.getRcvBufSize() > 0) { sock.setReceiveBufferSize(socketConfig.getRcvBufSize()); } if (socketConfig.getSndBufSize() > 0) { sock.setSendBufferSize(socketConfig.getSndBufSize()); } final int linger = socketConfig.getSoLinger().toMillisecondsIntBound(); if (linger >= 0) { sock.setSoLinger(true, linger); } conn.bind(sock); final InetSocketAddress remoteAddress = new InetSocketAddress(address, port); if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connecting {}->{} ({})", host.getHostName(), host.getPort(), localAddress, remoteAddress, connectTimeout); } try { sock = sf.connectSocket(sock, host, remoteAddress, localAddress, connectTimeout, attachment, context); conn.bind(sock); conn.setSocketTimeout(soTimeout); if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connected {}->{} as {}", host.getHostName(), host.getPort(), localAddress, remoteAddress, ConnPoolSupport.getId(conn)); } return; } catch (final IOException ex) { if (last) { if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connection to {} failed ({}); terminating operation", host.getHostName(), host.getPort(), remoteAddress, ex.getClass()); } throw ConnectExceptionSupport.enhance(ex, host, remoteAddresses); } else { if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connection to {} failed ({}); retrying connection to the next address", host.getHostName(), host.getPort(), remoteAddress, ex.getClass()); } } } } } @Override public void upgrade( final ManagedHttpClientConnection conn, final HttpHost host, final HttpContext context) throws IOException { upgrade(conn, host, null, context); } @Override public void upgrade( final ManagedHttpClientConnection conn, final HttpHost host, final Object attachment, final HttpContext context) throws IOException { final HttpClientContext clientContext = HttpClientContext.adapt(context); final Lookup registry = getSocketFactoryRegistry(clientContext); final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); if (sf == null) { throw new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported"); } if (!(sf instanceof LayeredConnectionSocketFactory)) { throw new UnsupportedSchemeException(host.getSchemeName() + " protocol does not support connection upgrade"); } final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf; Socket sock = conn.getSocket(); if (sock == null) { throw new ConnectionClosedException("Connection is closed"); } final int port = this.schemePortResolver.resolve(host); sock = lsf.createLayeredSocket(sock, host.getHostName(), port, attachment, context); conn.bind(sock); } } DefaultHttpResponseParserFactory.java000066400000000000000000000060141434266521000414610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.message.BasicLineParser; import org.apache.hc.core5.http.message.LineParser; /** * Default factory for response message parsers. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultHttpResponseParserFactory implements HttpMessageParserFactory { public static final DefaultHttpResponseParserFactory INSTANCE = new DefaultHttpResponseParserFactory(); private final LineParser lineParser; private final HttpResponseFactory responseFactory; public DefaultHttpResponseParserFactory( final LineParser lineParser, final HttpResponseFactory responseFactory) { super(); this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE; this.responseFactory = responseFactory != null ? responseFactory : DefaultClassicHttpResponseFactory.INSTANCE; } public DefaultHttpResponseParserFactory(final HttpResponseFactory responseFactory) { this(null, responseFactory); } public DefaultHttpResponseParserFactory() { this(null, null); } @Override public HttpMessageParser create(final Http1Config h1Config) { return new LenientHttpResponseParser(this.lineParser, this.responseFactory, h1Config); } } DefaultManagedHttpClientConnection.java000066400000000000000000000176611434266521000417030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.io.InterruptedIOException; import java.net.Socket; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection; import org.apache.hc.core5.http.impl.io.SocketHolder; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy; import org.apache.hc.core5.http.message.RequestLine; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; final class DefaultManagedHttpClientConnection extends DefaultBHttpClientConnection implements ManagedHttpClientConnection, Identifiable { private static final Logger LOG = LoggerFactory.getLogger(DefaultManagedHttpClientConnection.class); private static final Logger HEADER_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.headers"); private static final Logger WIRE_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.wire"); private final String id; private final AtomicBoolean closed; private Timeout socketTimeout; public DefaultManagedHttpClientConnection( final String id, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final Http1Config h1Config, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final ResponseOutOfOrderStrategy responseOutOfOrderStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { super( h1Config, charDecoder, charEncoder, incomingContentStrategy, outgoingContentStrategy, responseOutOfOrderStrategy, requestWriterFactory, responseParserFactory); this.id = id; this.closed = new AtomicBoolean(); } public DefaultManagedHttpClientConnection( final String id, final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, final Http1Config h1Config, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { this( id, charDecoder, charEncoder, h1Config, incomingContentStrategy, outgoingContentStrategy, null, requestWriterFactory, responseParserFactory); } public DefaultManagedHttpClientConnection(final String id) { this(id, null, null, null, null, null, null, null); } @Override public String getId() { return this.id; } @Override public void bind(final SocketHolder socketHolder) throws IOException { if (this.closed.get()) { final Socket socket = socketHolder.getSocket(); socket.close(); // allow this to throw... // ...but if it doesn't, explicitly throw one ourselves. throw new InterruptedIOException("Connection already shutdown"); } super.bind(socketHolder); socketTimeout = Timeout.ofMilliseconds(socketHolder.getSocket().getSoTimeout()); } @Override public Socket getSocket() { final SocketHolder socketHolder = getSocketHolder(); return socketHolder != null ? socketHolder.getSocket() : null; } @Override public SSLSession getSSLSession() { final Socket socket = getSocket(); if (socket instanceof SSLSocket) { return ((SSLSocket) socket).getSession(); } else { return null; } } @Override public void close() throws IOException { if (this.closed.compareAndSet(false, true)) { if (LOG.isDebugEnabled()) { LOG.debug("{} Close connection", this.id); } super.close(); } } @Override public void setSocketTimeout(final Timeout timeout) { if (LOG.isDebugEnabled()) { LOG.debug("{} set socket timeout to {}", this.id, timeout); } super.setSocketTimeout(timeout); } @Override public void close(final CloseMode closeMode) { if (this.closed.compareAndSet(false, true)) { if (LOG.isDebugEnabled()) { LOG.debug("{} close connection {}", this.id, closeMode); } super.close(closeMode); } } @Override public void bind(final Socket socket) throws IOException { super.bind(WIRE_LOG.isDebugEnabled() ? new LoggingSocketHolder(socket, this.id, WIRE_LOG) : new SocketHolder(socket)); socketTimeout = Timeout.ofMilliseconds(socket.getSoTimeout()); } @Override protected void onResponseReceived(final ClassicHttpResponse response) { if (response != null && HEADER_LOG.isDebugEnabled()) { HEADER_LOG.debug("{} << {}", this.id, new StatusLine(response)); final Header[] headers = response.getHeaders(); for (final Header header : headers) { HEADER_LOG.debug("{} << {}", this.id, header); } } } @Override protected void onRequestSubmitted(final ClassicHttpRequest request) { if (request != null && HEADER_LOG.isDebugEnabled()) { HEADER_LOG.debug("{} >> {}", this.id, new RequestLine(request)); final Header[] headers = request.getHeaders(); for (final Header header : headers) { HEADER_LOG.debug("{} >> {}", this.id, header); } } } @Override public void passivate() { super.setSocketTimeout(Timeout.ZERO_MILLISECONDS); } @Override public void activate() { super.setSocketTimeout(socketTimeout); } } LenientHttpResponseParser.java000066400000000000000000000070221434266521000401430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponseFactory; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParser; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.util.CharArrayBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Lenient HTTP response parser implementation that can skip malformed data until * a valid HTTP response message head is encountered. * * @since 4.2 */ public class LenientHttpResponseParser extends DefaultHttpResponseParser { private static final Logger LOG = LoggerFactory.getLogger(LenientHttpResponseParser.class); /** * Creates new instance of DefaultHttpResponseParser. * * @param lineParser the line parser. If {@code null} * {@link org.apache.hc.core5.http.message.BasicLineParser#INSTANCE} will be used. * @param responseFactory HTTP response factory. If {@code null} * {@link org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory#INSTANCE} * will be used. * @param h1Config HTTP/1.1 parameters. If {@code null}. {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public LenientHttpResponseParser( final LineParser lineParser, final HttpResponseFactory responseFactory, final Http1Config h1Config) { super(lineParser, responseFactory, h1Config); } /** * Creates new instance of DefaultHttpResponseParser. * * @param h1Config HTTP/1.1 parameters. If {@code null}. {@link Http1Config#DEFAULT} will be used. * * @since 4.3 */ public LenientHttpResponseParser(final Http1Config h1Config) { this(null, null, h1Config); } @Override protected ClassicHttpResponse createMessage(final CharArrayBuffer buffer) throws IOException { try { return super.createMessage(buffer); } catch (final HttpException ex) { if (LOG.isDebugEnabled()) { LOG.debug("Garbage in response: {}", buffer); } return null; } } } LoggingInputStream.java000066400000000000000000000077121434266521000366010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.io.InputStream; import org.apache.hc.client5.http.impl.Wire; class LoggingInputStream extends InputStream { private final InputStream in; private final Wire wire; public LoggingInputStream(final InputStream in, final Wire wire) { super(); this.in = in; this.wire = wire; } @Override public int read() throws IOException { try { final int b = in.read(); if (b == -1) { wire.input("end of stream"); } else { wire.input(b); } return b; } catch (final IOException ex) { wire.input("[read] I/O error: " + ex.getMessage()); throw ex; } } @Override public int read(final byte[] b) throws IOException { try { final int bytesRead = in.read(b); if (bytesRead == -1) { wire.input("end of stream"); } else if (bytesRead > 0) { wire.input(b, 0, bytesRead); } return bytesRead; } catch (final IOException ex) { wire.input("[read] I/O error: " + ex.getMessage()); throw ex; } } @Override public int read(final byte[] b, final int off, final int len) throws IOException { try { final int bytesRead = in.read(b, off, len); if (bytesRead == -1) { wire.input("end of stream"); } else if (bytesRead > 0) { wire.input(b, off, bytesRead); } return bytesRead; } catch (final IOException ex) { wire.input("[read] I/O error: " + ex.getMessage()); throw ex; } } @Override public long skip(final long n) throws IOException { try { return super.skip(n); } catch (final IOException ex) { wire.input("[skip] I/O error: " + ex.getMessage()); throw ex; } } @Override public int available() throws IOException { try { return in.available(); } catch (final IOException ex) { wire.input("[available] I/O error : " + ex.getMessage()); throw ex; } } @Override public void mark(final int readlimit) { super.mark(readlimit); } @Override public void reset() throws IOException { super.reset(); } @Override public boolean markSupported() { return false; } @Override public void close() throws IOException { try { in.close(); } catch (final IOException ex) { wire.input("[close] I/O error: " + ex.getMessage()); throw ex; } } } LoggingOutputStream.java000066400000000000000000000056641434266521000370060ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.io.OutputStream; import org.apache.hc.client5.http.impl.Wire; class LoggingOutputStream extends OutputStream { private final OutputStream out; private final Wire wire; public LoggingOutputStream(final OutputStream out, final Wire wire) { super(); this.out = out; this.wire = wire; } @Override public void write(final int b) throws IOException { try { out.write(b); wire.output(b); } catch (final IOException ex) { wire.output("[write] I/O error: " + ex.getMessage()); throw ex; } } @Override public void write(final byte[] b) throws IOException { try { wire.output(b); out.write(b); } catch (final IOException ex) { wire.output("[write] I/O error: " + ex.getMessage()); throw ex; } } @Override public void write(final byte[] b, final int off, final int len) throws IOException { try { wire.output(b, off, len); out.write(b, off, len); } catch (final IOException ex) { wire.output("[write] I/O error: " + ex.getMessage()); throw ex; } } @Override public void flush() throws IOException { try { out.flush(); } catch (final IOException ex) { wire.output("[flush] I/O error: " + ex.getMessage()); throw ex; } } @Override public void close() throws IOException { try { out.close(); } catch (final IOException ex) { wire.output("[close] I/O error: " + ex.getMessage()); throw ex; } } } LoggingSocketHolder.java000066400000000000000000000040221434266521000367030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import org.apache.hc.client5.http.impl.Wire; import org.apache.hc.core5.http.impl.io.SocketHolder; import org.slf4j.Logger; class LoggingSocketHolder extends SocketHolder { private final Wire wire; public LoggingSocketHolder(final Socket socket, final String id, final Logger log) { super(socket); this.wire = new Wire(log, id); } @Override protected InputStream getInputStream(final Socket socket) throws IOException { return new LoggingInputStream(super.getInputStream(socket), wire); } @Override protected OutputStream getOutputStream(final Socket socket) throws IOException { return new LoggingOutputStream(super.getOutputStream(socket), wire); } } ManagedHttpClientConnectionFactory.java000066400000000000000000000246341434266521000417240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.net.Socket; 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.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentLengthStrategy; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy; import org.apache.hc.core5.http.impl.io.DefaultHttpRequestWriterFactory; import org.apache.hc.core5.http.impl.io.NoResponseOutOfOrderStrategy; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy; /** * Factory for {@link ManagedHttpClientConnection} instances. * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class ManagedHttpClientConnectionFactory implements HttpConnectionFactory { private static final AtomicLong COUNTER = new AtomicLong(); public static final ManagedHttpClientConnectionFactory INSTANCE = new ManagedHttpClientConnectionFactory(); private final Http1Config h1Config; private final CharCodingConfig charCodingConfig; private final HttpMessageWriterFactory requestWriterFactory; private final HttpMessageParserFactory responseParserFactory; private final ContentLengthStrategy incomingContentStrategy; private final ContentLengthStrategy outgoingContentStrategy; private final ResponseOutOfOrderStrategy responseOutOfOrderStrategy; private ManagedHttpClientConnectionFactory( final Http1Config h1Config, final CharCodingConfig charCodingConfig, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy, final ResponseOutOfOrderStrategy responseOutOfOrderStrategy) { this.h1Config = h1Config != null ? h1Config : Http1Config.DEFAULT; this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT; this.requestWriterFactory = requestWriterFactory != null ? requestWriterFactory : DefaultHttpRequestWriterFactory.INSTANCE; this.responseParserFactory = responseParserFactory != null ? responseParserFactory : DefaultHttpResponseParserFactory.INSTANCE; this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy : DefaultContentLengthStrategy.INSTANCE; this.responseOutOfOrderStrategy = responseOutOfOrderStrategy != null ? responseOutOfOrderStrategy : NoResponseOutOfOrderStrategy.INSTANCE; } public ManagedHttpClientConnectionFactory( final Http1Config h1Config, final CharCodingConfig charCodingConfig, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory, final ContentLengthStrategy incomingContentStrategy, final ContentLengthStrategy outgoingContentStrategy) { this( h1Config, charCodingConfig, requestWriterFactory, responseParserFactory, incomingContentStrategy, outgoingContentStrategy, null); } public ManagedHttpClientConnectionFactory( final Http1Config h1Config, final CharCodingConfig charCodingConfig, final HttpMessageWriterFactory requestWriterFactory, final HttpMessageParserFactory responseParserFactory) { this(h1Config, charCodingConfig, requestWriterFactory, responseParserFactory, null, null); } public ManagedHttpClientConnectionFactory( final Http1Config h1Config, final CharCodingConfig charCodingConfig, final HttpMessageParserFactory responseParserFactory) { this(h1Config, charCodingConfig, null, responseParserFactory); } public ManagedHttpClientConnectionFactory() { this(null, null, null); } @Override public ManagedHttpClientConnection createConnection(final Socket socket) throws IOException { CharsetDecoder charDecoder = null; CharsetEncoder charEncoder = null; final Charset charset = this.charCodingConfig.getCharset(); final CodingErrorAction malformedInputAction = this.charCodingConfig.getMalformedInputAction() != null ? this.charCodingConfig.getMalformedInputAction() : CodingErrorAction.REPORT; final CodingErrorAction unmappableInputAction = this.charCodingConfig.getUnmappableInputAction() != null ? this.charCodingConfig.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 String id = "http-outgoing-" + COUNTER.getAndIncrement(); final DefaultManagedHttpClientConnection conn = new DefaultManagedHttpClientConnection( id, charDecoder, charEncoder, h1Config, incomingContentStrategy, outgoingContentStrategy, responseOutOfOrderStrategy, requestWriterFactory, responseParserFactory); if (socket != null) { conn.bind(socket); } return conn; } /** * Create a new {@link Builder}. * * @since 5.1 */ public static Builder builder() { return new Builder(); } /** * Builder for {@link ManagedHttpClientConnectionFactory}. * * @since 5.1 */ public static final class Builder { private Http1Config http1Config; private CharCodingConfig charCodingConfig; private ContentLengthStrategy incomingContentLengthStrategy; private ContentLengthStrategy outgoingContentLengthStrategy; private ResponseOutOfOrderStrategy responseOutOfOrderStrategy; private HttpMessageWriterFactory requestWriterFactory; private HttpMessageParserFactory responseParserFactory; private Builder() {} public Builder http1Config(final Http1Config http1Config) { this.http1Config = http1Config; return this; } public Builder charCodingConfig(final CharCodingConfig charCodingConfig) { this.charCodingConfig = charCodingConfig; return this; } public Builder incomingContentLengthStrategy(final ContentLengthStrategy incomingContentLengthStrategy) { this.incomingContentLengthStrategy = incomingContentLengthStrategy; return this; } public Builder outgoingContentLengthStrategy(final ContentLengthStrategy outgoingContentLengthStrategy) { this.outgoingContentLengthStrategy = outgoingContentLengthStrategy; return this; } public Builder responseOutOfOrderStrategy(final ResponseOutOfOrderStrategy responseOutOfOrderStrategy) { this.responseOutOfOrderStrategy = responseOutOfOrderStrategy; return this; } public Builder requestWriterFactory( final HttpMessageWriterFactory requestWriterFactory) { this.requestWriterFactory = requestWriterFactory; return this; } public Builder responseParserFactory( final HttpMessageParserFactory responseParserFactory) { this.responseParserFactory = responseParserFactory; return this; } public ManagedHttpClientConnectionFactory build() { return new ManagedHttpClientConnectionFactory( http1Config, charCodingConfig, requestWriterFactory, responseParserFactory, incomingContentLengthStrategy, outgoingContentLengthStrategy, responseOutOfOrderStrategy); } } } PoolingHttpClientConnectionManager.java000066400000000000000000000734521434266521000417440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.io.IOException; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.ConnectionShutdownException; import org.apache.hc.client5.http.impl.PrefixedIncrementingId; import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.io.HttpClientConnectionOperator; import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.pool.LaxConnPool; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolEntry; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.Deadline; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@code ClientConnectionPoolManager} maintains a pool of * {@link ManagedHttpClientConnection}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 serviced by leasing * a connection from the pool rather than creating a new connection. *

* {@code ClientConnectionPoolManager} maintains a maximum limit of connection * on a per route basis and in total. Connection limits, however, can be adjusted * using {@link ConnPoolControl} methods. *

* Total time to live (TTL) set at construction time defines maximum life span * of persistent connections regardless of their expiration setting. No persistent * connection will be re-used past its TTL value. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class PoolingHttpClientConnectionManager implements HttpClientConnectionManager, ConnPoolControl { private static final Logger LOG = LoggerFactory.getLogger(PoolingHttpClientConnectionManager.class); public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 25; public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5; private final HttpClientConnectionOperator connectionOperator; private final ManagedConnPool pool; private final HttpConnectionFactory connFactory; private final AtomicBoolean closed; private volatile Resolver socketConfigResolver; private volatile Resolver connectionConfigResolver; private volatile Resolver tlsConfigResolver; public PoolingHttpClientConnectionManager() { this(RegistryBuilder.create() .register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory()) .register(URIScheme.HTTPS.id, SSLConnectionSocketFactory.getSocketFactory()) .build()); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry) { this(socketFactoryRegistry, null); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, final HttpConnectionFactory connFactory) { this(socketFactoryRegistry, PoolConcurrencyPolicy.STRICT, TimeValue.NEG_ONE_MILLISECOND, connFactory); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, final PoolConcurrencyPolicy poolConcurrencyPolicy, final TimeValue timeToLive, final HttpConnectionFactory connFactory) { this(socketFactoryRegistry, poolConcurrencyPolicy, PoolReusePolicy.LIFO, timeToLive, connFactory); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, final PoolConcurrencyPolicy poolConcurrencyPolicy, final PoolReusePolicy poolReusePolicy, final TimeValue timeToLive) { this(socketFactoryRegistry, poolConcurrencyPolicy, poolReusePolicy, timeToLive, null); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, final PoolConcurrencyPolicy poolConcurrencyPolicy, final PoolReusePolicy poolReusePolicy, final TimeValue timeToLive, final HttpConnectionFactory connFactory) { this(socketFactoryRegistry, poolConcurrencyPolicy, poolReusePolicy, timeToLive, null, null, connFactory); } public PoolingHttpClientConnectionManager( final Registry socketFactoryRegistry, final PoolConcurrencyPolicy poolConcurrencyPolicy, final PoolReusePolicy poolReusePolicy, final TimeValue timeToLive, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver, final HttpConnectionFactory connFactory) { this(new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver), poolConcurrencyPolicy, poolReusePolicy, timeToLive, connFactory); } @Internal protected PoolingHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, final PoolConcurrencyPolicy poolConcurrencyPolicy, final PoolReusePolicy poolReusePolicy, final TimeValue timeToLive, final HttpConnectionFactory connFactory) { super(); this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator"); switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) { case STRICT: this.pool = new StrictConnPool( DEFAULT_MAX_CONNECTIONS_PER_ROUTE, DEFAULT_MAX_TOTAL_CONNECTIONS, timeToLive, poolReusePolicy, null) { @Override public void closeExpired() { enumAvailable(e -> closeIfExpired(e)); } }; break; case LAX: this.pool = new LaxConnPool( DEFAULT_MAX_CONNECTIONS_PER_ROUTE, timeToLive, poolReusePolicy, null) { @Override public void closeExpired() { enumAvailable(e -> closeIfExpired(e)); } }; break; default: throw new IllegalArgumentException("Unexpected PoolConcurrencyPolicy value: " + poolConcurrencyPolicy); } this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; this.closed = new AtomicBoolean(false); } @Internal protected PoolingHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, final ManagedConnPool pool, final HttpConnectionFactory connFactory) { super(); this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator"); this.pool = Args.notNull(pool, "Connection pool"); this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; this.closed = new AtomicBoolean(false); } @Override public void close() { close(CloseMode.GRACEFUL); } @Override public void close(final CloseMode closeMode) { if (this.closed.compareAndSet(false, true)) { if (LOG.isDebugEnabled()) { LOG.debug("Shutdown connection pool {}", closeMode); } this.pool.close(closeMode); LOG.debug("Connection pool shut down"); } } private InternalConnectionEndpoint cast(final ConnectionEndpoint endpoint) { if (endpoint instanceof InternalConnectionEndpoint) { return (InternalConnectionEndpoint) endpoint; } throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass()); } private SocketConfig resolveSocketConfig(final HttpRoute route) { final Resolver resolver = this.socketConfigResolver; final SocketConfig socketConfig = resolver != null ? resolver.resolve(route) : null; return socketConfig != null ? socketConfig : SocketConfig.DEFAULT; } private ConnectionConfig resolveConnectionConfig(final HttpRoute route) { final Resolver resolver = this.connectionConfigResolver; final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(route) : null; return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT; } private TlsConfig resolveTlsConfig(final HttpHost host) { final Resolver resolver = this.tlsConfigResolver; final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null; return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT; } private TimeValue resolveValidateAfterInactivity(final ConnectionConfig connectionConfig) { final TimeValue timeValue = connectionConfig.getValidateAfterInactivity(); return timeValue != null ? timeValue : TimeValue.ofSeconds(2); } public LeaseRequest lease(final String id, final HttpRoute route, final Object state) { return lease(id, route, Timeout.DISABLED, state); } @Override public LeaseRequest lease( final String id, final HttpRoute route, final Timeout requestTimeout, final Object state) { Args.notNull(route, "HTTP route"); if (LOG.isDebugEnabled()) { LOG.debug("{} endpoint lease request ({}) {}", id, requestTimeout, ConnPoolSupport.formatStats(route, state, pool)); } final Future> leaseFuture = this.pool.lease(route, state, requestTimeout, null); return new LeaseRequest() { private volatile ConnectionEndpoint endpoint; @Override public synchronized ConnectionEndpoint get( final Timeout timeout) throws InterruptedException, ExecutionException, TimeoutException { Args.notNull(timeout, "Operation timeout"); if (this.endpoint != null) { return this.endpoint; } final PoolEntry poolEntry; try { poolEntry = leaseFuture.get(timeout.getDuration(), timeout.getTimeUnit()); } catch (final TimeoutException ex) { leaseFuture.cancel(true); throw ex; } if (LOG.isDebugEnabled()) { LOG.debug("{} endpoint leased {}", id, ConnPoolSupport.formatStats(route, state, pool)); } final ConnectionConfig connectionConfig = resolveConnectionConfig(route); try { if (poolEntry.hasConnection()) { final TimeValue timeToLive = connectionConfig.getTimeToLive(); if (TimeValue.isNonNegative(timeToLive)) { final Deadline deadline = Deadline.calculate(poolEntry.getCreated(), timeToLive); if (deadline.isExpired()) { poolEntry.discardConnection(CloseMode.GRACEFUL); } } } if (poolEntry.hasConnection()) { final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig); if (TimeValue.isNonNegative(timeValue)) { final Deadline deadline = Deadline.calculate(poolEntry.getUpdated(), timeValue); if (deadline.isExpired()) { final ManagedHttpClientConnection conn = poolEntry.getConnection(); boolean stale; try { stale = conn.isStale(); } catch (final IOException ignore) { stale = true; } if (stale) { if (LOG.isDebugEnabled()) { LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn)); } poolEntry.discardConnection(CloseMode.IMMEDIATE); } } } } final ManagedHttpClientConnection conn = poolEntry.getConnection(); if (conn != null) { conn.activate(); } else { poolEntry.assignConnection(connFactory.createConnection(null)); } this.endpoint = new InternalConnectionEndpoint(poolEntry); if (LOG.isDebugEnabled()) { LOG.debug("{} acquired {}", id, ConnPoolSupport.getId(endpoint)); } return this.endpoint; } catch (final Exception ex) { if (LOG.isDebugEnabled()) { LOG.debug("{} endpoint lease failed", id); } pool.release(poolEntry, false); throw new ExecutionException(ex.getMessage(), ex); } } @Override public boolean cancel() { return leaseFuture.cancel(true); } }; } @Override public void release(final ConnectionEndpoint endpoint, final Object state, final TimeValue keepAlive) { Args.notNull(endpoint, "Managed endpoint"); final PoolEntry entry = cast(endpoint).detach(); if (entry == null) { return; } if (LOG.isDebugEnabled()) { LOG.debug("{} releasing endpoint", ConnPoolSupport.getId(endpoint)); } final ManagedHttpClientConnection conn = entry.getConnection(); if (conn != null && keepAlive == null) { conn.close(CloseMode.GRACEFUL); } boolean reusable = conn != null && conn.isOpen() && conn.isConsistent(); try { if (reusable) { entry.updateState(state); entry.updateExpiry(keepAlive); conn.passivate(); if (LOG.isDebugEnabled()) { final String s; if (TimeValue.isPositive(keepAlive)) { s = "for " + keepAlive; } else { s = "indefinitely"; } LOG.debug("{} connection {} can be kept alive {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn), s); } } else { if (LOG.isDebugEnabled()) { LOG.debug("{} connection is not kept alive", ConnPoolSupport.getId(endpoint)); } } } catch (final RuntimeException ex) { reusable = false; throw ex; } finally { this.pool.release(entry, reusable); if (LOG.isDebugEnabled()) { LOG.debug("{} connection released {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.formatStats(entry.getRoute(), entry.getState(), pool)); } } } @Override public void connect(final ConnectionEndpoint endpoint, final TimeValue timeout, final HttpContext context) throws IOException { Args.notNull(endpoint, "Managed endpoint"); final InternalConnectionEndpoint internalEndpoint = cast(endpoint); if (internalEndpoint.isConnected()) { return; } final PoolEntry poolEntry = internalEndpoint.getPoolEntry(); if (!poolEntry.hasConnection()) { poolEntry.assignConnection(connFactory.createConnection(null)); } final HttpRoute route = poolEntry.getRoute(); final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost(); final SocketConfig socketConfig = resolveSocketConfig(route); final ConnectionConfig connectionConfig = resolveConnectionConfig(route); final TlsConfig tlsConfig = resolveTlsConfig(host); final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : connectionConfig.getConnectTimeout(); if (LOG.isDebugEnabled()) { LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout); } final ManagedHttpClientConnection conn = poolEntry.getConnection(); this.connectionOperator.connect( conn, host, route.getLocalSocketAddress(), connectTimeout, socketConfig, tlsConfig, context); if (LOG.isDebugEnabled()) { LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn)); } final Timeout socketTimeout = connectionConfig.getSocketTimeout(); if (socketTimeout != null) { conn.setSocketTimeout(socketTimeout); } } @Override public void upgrade(final ConnectionEndpoint endpoint, final HttpContext context) throws IOException { Args.notNull(endpoint, "Managed endpoint"); final InternalConnectionEndpoint internalEndpoint = cast(endpoint); final PoolEntry poolEntry = internalEndpoint.getValidatedPoolEntry(); final HttpRoute route = poolEntry.getRoute(); final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost(); final TlsConfig tlsConfig = resolveTlsConfig(host); this.connectionOperator.upgrade(poolEntry.getConnection(), route.getTargetHost(), tlsConfig, context); } @Override public void closeIdle(final TimeValue idleTime) { Args.notNull(idleTime, "Idle time"); if (LOG.isDebugEnabled()) { LOG.debug("Closing connections idle longer than {}", idleTime); } this.pool.closeIdle(idleTime); } @Override public void closeExpired() { LOG.debug("Closing expired connections"); this.pool.closeExpired(); } @Override public Set getRoutes() { return this.pool.getRoutes(); } @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); } /** * Sets the same {@link SocketConfig} for all routes */ public void setDefaultSocketConfig(final SocketConfig config) { this.socketConfigResolver = (route) -> config; } /** * Sets {@link Resolver} of {@link SocketConfig} on a per route basis. * * @since 5.2 */ public void setSocketConfigResolver(final Resolver socketConfigResolver) { this.socketConfigResolver = socketConfigResolver; } /** * Sets the same {@link ConnectionConfig} for all routes * * @since 5.2 */ public void setDefaultConnectionConfig(final ConnectionConfig config) { this.connectionConfigResolver = (route) -> config; } /** * Sets {@link Resolver} of {@link ConnectionConfig} on a per route basis. * * @since 5.2 */ public void setConnectionConfigResolver(final Resolver connectionConfigResolver) { this.connectionConfigResolver = connectionConfigResolver; } /** * Sets the same {@link ConnectionConfig} for all hosts * * @since 5.2 */ public void setDefaultTlsConfig(final TlsConfig config) { this.tlsConfigResolver = (host) -> config; } /** * Sets {@link Resolver} of {@link TlsConfig} on a per host basis. * * @since 5.2 */ public void setTlsConfigResolver(final Resolver tlsConfigResolver) { this.tlsConfigResolver = tlsConfigResolver; } void closeIfExpired(final PoolEntry entry) { final long now = System.currentTimeMillis(); if (entry.getExpiryDeadline().isBefore(now)) { entry.discardConnection(CloseMode.GRACEFUL); } else { final ConnectionConfig connectionConfig = resolveConnectionConfig(entry.getRoute()); final TimeValue timeToLive = connectionConfig.getTimeToLive(); if (timeToLive != null && Deadline.calculate(entry.getCreated(), timeToLive).isBefore(now)) { entry.discardConnection(CloseMode.GRACEFUL); } } } /** * @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)} */ @Deprecated public SocketConfig getDefaultSocketConfig() { return SocketConfig.DEFAULT; } /** * @since 4.4 * * @deprecated Use {@link #setConnectionConfigResolver(Resolver)}. */ @Deprecated public TimeValue getValidateAfterInactivity() { return ConnectionConfig.DEFAULT.getValidateAfterInactivity(); } /** * Defines period of inactivity after which persistent connections must * be re-validated prior to being {@link #lease(String, HttpRoute, Object)} leased} to the consumer. * Negative values passed to this method disable connection validation. This check helps * detect connections that have become stale (half-closed) while kept inactive in the pool. * * @since 4.4 * * @deprecated Use {@link #setConnectionConfigResolver(Resolver)}. */ @Deprecated public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) { setDefaultConnectionConfig(ConnectionConfig.custom() .setValidateAfterInactivity(validateAfterInactivity) .build()); } private static final PrefixedIncrementingId INCREMENTING_ID = new PrefixedIncrementingId("ep-"); class InternalConnectionEndpoint extends ConnectionEndpoint implements Identifiable { private final AtomicReference> poolEntryRef; private final String id; InternalConnectionEndpoint( final PoolEntry poolEntry) { this.poolEntryRef = new AtomicReference<>(poolEntry); this.id = INCREMENTING_ID.getNextId(); } @Override public String getId() { return id; } PoolEntry getPoolEntry() { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry == null) { throw new ConnectionShutdownException(); } return poolEntry; } PoolEntry getValidatedPoolEntry() { final PoolEntry poolEntry = getPoolEntry(); final ManagedHttpClientConnection connection = poolEntry.getConnection(); Asserts.check(connection != null && connection.isOpen(), "Endpoint is not connected"); return poolEntry; } PoolEntry detach() { return poolEntryRef.getAndSet(null); } @Override public void close(final CloseMode closeMode) { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry != null) { poolEntry.discardConnection(closeMode); } } @Override public void close() throws IOException { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry != null) { poolEntry.discardConnection(CloseMode.GRACEFUL); } } @Override public boolean isConnected() { final PoolEntry poolEntry = getPoolEntry(); final ManagedHttpClientConnection connection = poolEntry.getConnection(); return connection != null && connection.isOpen(); } @Override public void setSocketTimeout(final Timeout timeout) { getValidatedPoolEntry().getConnection().setSocketTimeout(timeout); } @Override public ClassicHttpResponse execute( final String exchangeId, final ClassicHttpRequest request, final HttpRequestExecutor requestExecutor, final HttpContext context) throws IOException, HttpException { Args.notNull(request, "HTTP request"); Args.notNull(requestExecutor, "Request executor"); final ManagedHttpClientConnection connection = getValidatedPoolEntry().getConnection(); if (LOG.isDebugEnabled()) { LOG.debug("{} executing exchange {} over {}", id, exchangeId, ConnPoolSupport.getId(connection)); } return requestExecutor.execute(request, connection, context); } } } PoolingHttpClientConnectionManagerBuilder.java000066400000000000000000000251421434266521000432440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.util.TimeValue; /** * Builder for {@link PoolingHttpClientConnectionManager} instances. *

* When a particular component is not explicitly set 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()}. *

*
    *
  • ssl.TrustManagerFactory.algorithm
  • *
  • javax.net.ssl.trustStoreType
  • *
  • javax.net.ssl.trustStore
  • *
  • javax.net.ssl.trustStoreProvider
  • *
  • javax.net.ssl.trustStorePassword
  • *
  • ssl.KeyManagerFactory.algorithm
  • *
  • javax.net.ssl.keyStoreType
  • *
  • javax.net.ssl.keyStore
  • *
  • javax.net.ssl.keyStoreProvider
  • *
  • javax.net.ssl.keyStorePassword
  • *
  • https.protocols
  • *
  • https.cipherSuites
  • *
* * @since 5.0 */ public class PoolingHttpClientConnectionManagerBuilder { private HttpConnectionFactory connectionFactory; private LayeredConnectionSocketFactory sslSocketFactory; private SchemePortResolver schemePortResolver; private DnsResolver dnsResolver; private PoolConcurrencyPolicy poolConcurrencyPolicy; private PoolReusePolicy poolReusePolicy; private Resolver socketConfigResolver; private Resolver connectionConfigResolver; private Resolver tlsConfigResolver; private boolean systemProperties; private int maxConnTotal; private int maxConnPerRoute; public static PoolingHttpClientConnectionManagerBuilder create() { return new PoolingHttpClientConnectionManagerBuilder(); } PoolingHttpClientConnectionManagerBuilder() { super(); } /** * Assigns {@link HttpConnectionFactory} instance. */ public final PoolingHttpClientConnectionManagerBuilder setConnectionFactory( final HttpConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; return this; } /** * Assigns {@link LayeredConnectionSocketFactory} instance. */ public final PoolingHttpClientConnectionManagerBuilder setSSLSocketFactory( final LayeredConnectionSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; return this; } /** * Assigns {@link DnsResolver} instance. */ public final PoolingHttpClientConnectionManagerBuilder setDnsResolver(final DnsResolver dnsResolver) { this.dnsResolver = dnsResolver; return this; } /** * Assigns {@link SchemePortResolver} instance. */ public final PoolingHttpClientConnectionManagerBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) { this.schemePortResolver = schemePortResolver; return this; } /** * Assigns {@link PoolConcurrencyPolicy} value. */ public final PoolingHttpClientConnectionManagerBuilder setPoolConcurrencyPolicy(final PoolConcurrencyPolicy poolConcurrencyPolicy) { this.poolConcurrencyPolicy = poolConcurrencyPolicy; return this; } /** * Assigns {@link PoolReusePolicy} value. */ public final PoolingHttpClientConnectionManagerBuilder setConnPoolPolicy(final PoolReusePolicy poolReusePolicy) { this.poolReusePolicy = poolReusePolicy; return this; } /** * Assigns maximum total connection value. */ public final PoolingHttpClientConnectionManagerBuilder setMaxConnTotal(final int maxConnTotal) { this.maxConnTotal = maxConnTotal; return this; } /** * Assigns maximum connection per route value. */ public final PoolingHttpClientConnectionManagerBuilder setMaxConnPerRoute(final int maxConnPerRoute) { this.maxConnPerRoute = maxConnPerRoute; return this; } /** * Assigns the same {@link SocketConfig} for all routes. */ public final PoolingHttpClientConnectionManagerBuilder setDefaultSocketConfig(final SocketConfig config) { this.socketConfigResolver = (route) -> config; return this; } /** * Assigns {@link Resolver} of {@link SocketConfig} on a per route basis. * * @since 5.2 */ public final PoolingHttpClientConnectionManagerBuilder setSocketConfigResolver( final Resolver socketConfigResolver) { this.socketConfigResolver = socketConfigResolver; return this; } /** * Assigns the same {@link ConnectionConfig} for all routes. * * @since 5.2 */ public final PoolingHttpClientConnectionManagerBuilder setDefaultConnectionConfig(final ConnectionConfig config) { this.connectionConfigResolver = (route) -> config; return this; } /** * Assigns {@link Resolver} of {@link ConnectionConfig} on a per route basis. * * @since 5.2 */ public final PoolingHttpClientConnectionManagerBuilder setConnectionConfigResolver( final Resolver connectionConfigResolver) { this.connectionConfigResolver = connectionConfigResolver; return this; } /** * Assigns the same {@link TlsConfig} for all hosts. * * @since 5.2 */ public final PoolingHttpClientConnectionManagerBuilder setDefaultTlsConfig(final TlsConfig config) { this.tlsConfigResolver = (host) -> config; return this; } /** * Assigns {@link Resolver} of {@link TlsConfig} on a per host basis. * * @since 5.2 */ public final PoolingHttpClientConnectionManagerBuilder setTlsConfigResolver( final Resolver tlsConfigResolver) { this.tlsConfigResolver = tlsConfigResolver; return this; } /** * Sets maximum time to live for persistent connections * * @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)}. */ @Deprecated public final PoolingHttpClientConnectionManagerBuilder setConnectionTimeToLive(final TimeValue timeToLive) { setDefaultConnectionConfig(ConnectionConfig.custom() .setTimeToLive(timeToLive) .build()); return this; } /** * Sets period after inactivity after which persistent * connections must be checked to ensure they are still valid. * * @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)}. */ @Deprecated public final PoolingHttpClientConnectionManagerBuilder setValidateAfterInactivity(final TimeValue validateAfterInactivity) { setDefaultConnectionConfig(ConnectionConfig.custom() .setValidateAfterInactivity(validateAfterInactivity) .build()); return this; } /** * Use system properties when creating and configuring default * implementations. */ public final PoolingHttpClientConnectionManagerBuilder useSystemProperties() { this.systemProperties = true; return this; } public PoolingHttpClientConnectionManager build() { @SuppressWarnings("resource") final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager( RegistryBuilder.create() .register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory()) .register(URIScheme.HTTPS.id, sslSocketFactory != null ? sslSocketFactory : (systemProperties ? SSLConnectionSocketFactory.getSystemSocketFactory() : SSLConnectionSocketFactory.getSocketFactory())) .build(), poolConcurrencyPolicy, poolReusePolicy, null, schemePortResolver, dnsResolver, connectionFactory); poolingmgr.setSocketConfigResolver(socketConfigResolver); poolingmgr.setConnectionConfigResolver(connectionConfigResolver); poolingmgr.setTlsConfigResolver(tlsConfigResolver); if (maxConnTotal > 0) { poolingmgr.setMaxTotal(maxConnTotal); } if (maxConnPerRoute > 0) { poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute); } return poolingmgr; } } package-info.java000066400000000000000000000024601434266521000353360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client connection management functions based the classic * connection management APIs. */ package org.apache.hc.client5.http.impl.io; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/000077500000000000000000000000001434266521000324025ustar00rootroot00000000000000DefaultAsyncClientConnectionOperator.java000066400000000000000000000210601434266521000424420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.nio; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.concurrent.Future; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.nio.AsyncClientConnectionOperator; import org.apache.hc.client5.http.nio.ManagedAsyncClientConnection; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.concurrent.FutureContribution; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectionOperator { private final SchemePortResolver schemePortResolver; private final MultihomeIOSessionRequester sessionRequester; private final Lookup tlsStrategyLookup; DefaultAsyncClientConnectionOperator( final Lookup tlsStrategyLookup, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver) { this.tlsStrategyLookup = Args.notNull(tlsStrategyLookup, "TLS strategy lookup"); this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; this.sessionRequester = new MultihomeIOSessionRequester(dnsResolver); } @Override public Future connect( final ConnectionInitiator connectionInitiator, final HttpHost host, final SocketAddress localAddress, final Timeout connectTimeout, final Object attachment, final FutureCallback callback) { return connect(connectionInitiator, host, localAddress, connectTimeout, attachment, null, callback); } @Override public Future connect( final ConnectionInitiator connectionInitiator, final HttpHost host, final SocketAddress localAddress, final Timeout connectTimeout, final Object attachment, final HttpContext context, final FutureCallback callback) { Args.notNull(connectionInitiator, "Connection initiator"); Args.notNull(host, "Host"); final ComplexFuture future = new ComplexFuture<>(callback); final HttpHost remoteEndpoint = RoutingSupport.normalize(host, schemePortResolver); final InetAddress remoteAddress = host.getAddress(); final TlsStrategy tlsStrategy = tlsStrategyLookup != null ? tlsStrategyLookup.lookup(host.getSchemeName()) : null; final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT; final Future sessionFuture = sessionRequester.connect( connectionInitiator, remoteEndpoint, remoteAddress != null ? new InetSocketAddress(remoteAddress, remoteEndpoint.getPort()) : null, localAddress, connectTimeout, tlsConfig.getHttpVersionPolicy(), new FutureCallback() { @Override public void completed(final IOSession session) { final DefaultManagedAsyncClientConnection connection = new DefaultManagedAsyncClientConnection(session); if (tlsStrategy != null && URIScheme.HTTPS.same(host.getSchemeName())) { try { final Timeout socketTimeout = connection.getSocketTimeout(); final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout(); tlsStrategy.upgrade( connection, host, attachment, handshakeTimeout != null ? handshakeTimeout : connectTimeout, new FutureContribution(future) { @Override public void completed(final TransportSecurityLayer transportSecurityLayer) { connection.setSocketTimeout(socketTimeout); future.completed(connection); } }); } catch (final Exception ex) { future.failed(ex); } } else { future.completed(connection); } } @Override public void failed(final Exception ex) { future.failed(ex); } @Override public void cancelled() { future.cancel(); } }); future.setDependency(sessionFuture); return future; } @Override public void upgrade( final ManagedAsyncClientConnection connection, final HttpHost host, final Object attachment) { upgrade(connection, host, attachment, null, null); } @Override public void upgrade( final ManagedAsyncClientConnection connection, final HttpHost host, final Object attachment, final HttpContext context) { upgrade(connection, host, attachment, context, null); } @Override public void upgrade( final ManagedAsyncClientConnection connection, final HttpHost host, final Object attachment, final HttpContext context, final FutureCallback callback) { final TlsStrategy tlsStrategy = tlsStrategyLookup != null ? tlsStrategyLookup.lookup(host.getSchemeName()) : null; if (tlsStrategy != null) { tlsStrategy.upgrade( connection, host, attachment, null, new CallbackContribution(callback) { @Override public void completed(final TransportSecurityLayer transportSecurityLayer) { if (callback != null) { callback.completed(connection); } } }); } } } DefaultManagedAsyncClientConnection.java000066400000000000000000000167751434266521000422240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.nio; import java.io.IOException; import java.net.SocketAddress; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import org.apache.hc.client5.http.nio.ManagedAsyncClientConnection; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.EndpointDetails; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.nio.command.ShutdownCommand; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer; import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; final class DefaultManagedAsyncClientConnection implements ManagedAsyncClientConnection, Identifiable { private static final Logger LOG = LoggerFactory.getLogger(DefaultManagedAsyncClientConnection.class); private final IOSession ioSession; private final Timeout socketTimeout; private final AtomicBoolean closed; public DefaultManagedAsyncClientConnection(final IOSession ioSession) { this.ioSession = ioSession; this.socketTimeout = ioSession.getSocketTimeout(); this.closed = new AtomicBoolean(); } @Override public String getId() { return ioSession.getId(); } @Override public void close(final CloseMode closeMode) { if (this.closed.compareAndSet(false, true)) { if (LOG.isDebugEnabled()) { LOG.debug("{} Shutdown connection {}", getId(), closeMode); } ioSession.close(closeMode); } } @Override public void close() throws IOException { if (this.closed.compareAndSet(false, true)) { if (LOG.isDebugEnabled()) { LOG.debug("{} Close connection", getId()); } ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.IMMEDIATE); } } @Override public boolean isOpen() { return ioSession.isOpen(); } @Override public void setSocketTimeout(final Timeout timeout) { ioSession.setSocketTimeout(timeout); } @Override public Timeout getSocketTimeout() { return ioSession.getSocketTimeout(); } @Override public SocketAddress getRemoteAddress() { return ioSession.getRemoteAddress(); } @Override public SocketAddress getLocalAddress() { return ioSession.getLocalAddress(); } @Override public EndpointDetails getEndpointDetails() { final IOEventHandler handler = ioSession.getHandler(); if (handler instanceof HttpConnection) { return ((HttpConnection) handler).getEndpointDetails(); } return null; } @Override public ProtocolVersion getProtocolVersion() { final IOEventHandler handler = ioSession.getHandler(); if (handler instanceof HttpConnection) { final ProtocolVersion version = ((HttpConnection) handler).getProtocolVersion(); if (version != null) { return version; } } return HttpVersion.DEFAULT; } @Override public void startTls( final SSLContext sslContext, final NamedEndpoint endpoint, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier, final Timeout handshakeTimeout, final FutureCallback callback) throws UnsupportedOperationException { if (LOG.isDebugEnabled()) { LOG.debug("{} start TLS", getId()); } if (ioSession instanceof TransportSecurityLayer) { ((TransportSecurityLayer) ioSession).startTls(sslContext, endpoint, sslBufferMode, initializer, verifier, handshakeTimeout, callback); } else { throw new UnsupportedOperationException("TLS upgrade not supported"); } } @Override public void startTls( final SSLContext sslContext, final NamedEndpoint endpoint, final SSLBufferMode sslBufferMode, final SSLSessionInitializer initializer, final SSLSessionVerifier verifier, final Timeout handshakeTimeout) throws UnsupportedOperationException { startTls(sslContext, endpoint, sslBufferMode, initializer, verifier, handshakeTimeout, null); } @Override public TlsDetails getTlsDetails() { return ioSession instanceof TransportSecurityLayer ? ((TransportSecurityLayer) ioSession).getTlsDetails() : null; } @Override public SSLSession getSSLSession() { final TlsDetails tlsDetails = getTlsDetails(); return tlsDetails != null ? tlsDetails.getSSLSession() : null; } @Override public void submitCommand(final Command command, final Command.Priority priority) { if (LOG.isDebugEnabled()) { LOG.debug("{} {} with {} priority", getId(), command.getClass().getSimpleName(), priority); } ioSession.enqueue(command, Command.Priority.IMMEDIATE); } @Override public void passivate() { ioSession.setSocketTimeout(Timeout.ZERO_MILLISECONDS); } @Override public void activate() { ioSession.setSocketTimeout(socketTimeout); } @Override public void switchProtocol(final String protocolId, final FutureCallback callback) throws UnsupportedOperationException { if (ioSession instanceof ProtocolIOSession) { ((ProtocolIOSession) ioSession).switchProtocol(protocolId, callback); } else { throw new UnsupportedOperationException("Protocol switch not supported"); } } } MultihomeConnectionInitiator.java000066400000000000000000000065311434266521000410410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.nio; import java.net.SocketAddress; import java.util.concurrent.Future; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; /** * Multi-home DNS aware implementation of {@link ConnectionInitiator}. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public final class MultihomeConnectionInitiator implements ConnectionInitiator { private final ConnectionInitiator connectionInitiator; private final MultihomeIOSessionRequester sessionRequester; public MultihomeConnectionInitiator( final ConnectionInitiator connectionInitiator, final DnsResolver dnsResolver) { this.connectionInitiator = Args.notNull(connectionInitiator, "Connection initiator"); this.sessionRequester = new MultihomeIOSessionRequester(dnsResolver); } @Override public Future connect( final NamedEndpoint remoteEndpoint, final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout connectTimeout, final Object attachment, final FutureCallback callback) { Args.notNull(remoteEndpoint, "Remote endpoint"); return sessionRequester.connect(connectionInitiator, remoteEndpoint, remoteAddress, localAddress, connectTimeout, attachment, callback); } public Future connect( final NamedEndpoint remoteEndpoint, final SocketAddress localAddress, final Timeout connectTimeout, final Object attachment, final FutureCallback callback) { Args.notNull(remoteEndpoint, "Remote endpoint"); return sessionRequester.connect(connectionInitiator, remoteEndpoint, localAddress, connectTimeout, attachment, callback); } } MultihomeIOSessionRequester.java000066400000000000000000000213141434266521000406260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.nio; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hc.client5.http.ConnectExceptionSupport; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; final class MultihomeIOSessionRequester { private static final Logger LOG = LoggerFactory.getLogger(MultihomeIOSessionRequester.class); private final DnsResolver dnsResolver; MultihomeIOSessionRequester(final DnsResolver dnsResolver) { this.dnsResolver = dnsResolver != null ? dnsResolver : SystemDefaultDnsResolver.INSTANCE; } public Future connect( final ConnectionInitiator connectionInitiator, final NamedEndpoint remoteEndpoint, final SocketAddress remoteAddress, final SocketAddress localAddress, final Timeout connectTimeout, final Object attachment, final FutureCallback callback) { final ComplexFuture future = new ComplexFuture<>(callback); if (remoteAddress != null) { if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connecting {} to {} ({})", remoteEndpoint.getHostName(), remoteEndpoint.getPort(), localAddress, remoteAddress, connectTimeout); } final Future sessionFuture = connectionInitiator.connect(remoteEndpoint, remoteAddress, localAddress, connectTimeout, attachment, new FutureCallback() { @Override public void completed(final IOSession session) { future.completed(session); } @Override public void failed(final Exception cause) { if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connection to {} failed ({}); terminating operation", remoteEndpoint.getHostName(), remoteEndpoint.getPort(), remoteAddress, cause.getClass()); } if (cause instanceof IOException) { future.failed(ConnectExceptionSupport.enhance((IOException) cause, remoteEndpoint, (remoteAddress instanceof InetSocketAddress) ? new InetAddress[] { ((InetSocketAddress) remoteAddress).getAddress() } : new InetAddress[] {})); } else { future.failed(cause); } } @Override public void cancelled() { future.cancel(); } }); future.setDependency(sessionFuture); return future; } if (LOG.isDebugEnabled()) { LOG.debug("{} resolving remote address", remoteEndpoint.getHostName()); } final InetAddress[] remoteAddresses; try { remoteAddresses = dnsResolver.resolve(remoteEndpoint.getHostName()); } catch (final UnknownHostException ex) { future.failed(ex); return future; } if (LOG.isDebugEnabled()) { LOG.debug("{} resolved to {}", remoteEndpoint.getHostName(), Arrays.asList(remoteAddresses)); } final Runnable runnable = new Runnable() { private final AtomicInteger attempt = new AtomicInteger(0); void executeNext() { final int index = attempt.getAndIncrement(); final InetSocketAddress remoteAddress = new InetSocketAddress(remoteAddresses[index], remoteEndpoint.getPort()); if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connecting {}->{} ({})", remoteEndpoint.getHostName(), remoteEndpoint.getPort(), localAddress, remoteAddress, connectTimeout); } final Future sessionFuture = connectionInitiator.connect( remoteEndpoint, remoteAddress, localAddress, connectTimeout, attachment, new FutureCallback() { @Override public void completed(final IOSession session) { if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connected {}->{} as {}", remoteEndpoint.getHostName(), remoteEndpoint.getPort(), localAddress, remoteAddress, session.getId()); } future.completed(session); } @Override public void failed(final Exception cause) { if (attempt.get() >= remoteAddresses.length) { if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connection to {} failed ({}); terminating operation", remoteEndpoint.getHostName(), remoteEndpoint.getPort(), remoteAddress, cause.getClass()); } if (cause instanceof IOException) { future.failed(ConnectExceptionSupport.enhance((IOException) cause, remoteEndpoint, remoteAddresses)); } else { future.failed(cause); } } else { if (LOG.isDebugEnabled()) { LOG.debug("{}:{} connection to {} failed ({}); retrying connection to the next address", remoteEndpoint.getHostName(), remoteEndpoint.getPort(), remoteAddress, cause.getClass()); } executeNext(); } } @Override public void cancelled() { future.cancel(); } }); future.setDependency(sessionFuture); } @Override public void run() { executeNext(); } }; runnable.run(); return future; } public Future connect( final ConnectionInitiator connectionInitiator, final NamedEndpoint remoteEndpoint, final SocketAddress localAddress, final Timeout connectTimeout, final Object attachment, final FutureCallback callback) { return connect(connectionInitiator, remoteEndpoint, null, localAddress, connectTimeout, attachment, callback); } } PoolingAsyncClientConnectionManager.java000066400000000000000000001011261434266521000422460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.nio; import java.net.InetSocketAddress; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.ConnPoolSupport; import org.apache.hc.client5.http.impl.ConnectionShutdownException; import org.apache.hc.client5.http.impl.PrefixedIncrementingId; import org.apache.hc.client5.http.nio.AsyncClientConnectionManager; import org.apache.hc.client5.http.nio.AsyncClientConnectionOperator; import org.apache.hc.client5.http.nio.AsyncConnectionEndpoint; import org.apache.hc.client5.http.nio.ManagedAsyncClientConnection; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.CallbackContribution; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.command.RequestExecutionCommand; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.nio.command.PingCommand; import org.apache.hc.core5.http2.nio.support.BasicPingHandler; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.pool.LaxConnPool; import org.apache.hc.core5.pool.ManagedConnPool; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolEntry; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Deadline; import org.apache.hc.core5.util.Identifiable; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@code PoolingAsyncClientConnectionManager} maintains a pool of non-blocking * {@link org.apache.hc.core5.http.HttpConnection}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 new connection. *

* {@code PoolingAsyncClientConnectionManager} maintains a maximum limit * of connection on a per route basis and in total. Connection limits * can be adjusted using {@link ConnPoolControl} methods. *

* Total time to live (TTL) set at construction time defines maximum life span * of persistent connections regardless of their expiration setting. No persistent * connection will be re-used past its TTL value. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL) public class PoolingAsyncClientConnectionManager implements AsyncClientConnectionManager, ConnPoolControl { private static final Logger LOG = LoggerFactory.getLogger(PoolingAsyncClientConnectionManager.class); public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 25; public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5; private final ManagedConnPool pool; private final AsyncClientConnectionOperator connectionOperator; private final AtomicBoolean closed; private volatile Resolver connectionConfigResolver; private volatile Resolver tlsConfigResolver; public PoolingAsyncClientConnectionManager() { this(RegistryBuilder.create() .register(URIScheme.HTTPS.getId(), DefaultClientTlsStrategy.getDefault()) .build()); } public PoolingAsyncClientConnectionManager(final Lookup tlsStrategyLookup) { this(tlsStrategyLookup, PoolConcurrencyPolicy.STRICT, TimeValue.NEG_ONE_MILLISECOND); } public PoolingAsyncClientConnectionManager( final Lookup tlsStrategyLookup, final PoolConcurrencyPolicy poolConcurrencyPolicy, final TimeValue timeToLive) { this(tlsStrategyLookup, poolConcurrencyPolicy, PoolReusePolicy.LIFO, timeToLive); } public PoolingAsyncClientConnectionManager( final Lookup tlsStrategyLookup, final PoolConcurrencyPolicy poolConcurrencyPolicy, final PoolReusePolicy poolReusePolicy, final TimeValue timeToLive) { this(tlsStrategyLookup, poolConcurrencyPolicy, poolReusePolicy, timeToLive, null, null); } public PoolingAsyncClientConnectionManager( final Lookup tlsStrategyLookup, final PoolConcurrencyPolicy poolConcurrencyPolicy, final PoolReusePolicy poolReusePolicy, final TimeValue timeToLive, final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver) { this(new DefaultAsyncClientConnectionOperator(tlsStrategyLookup, schemePortResolver, dnsResolver), poolConcurrencyPolicy, poolReusePolicy, timeToLive); } @Internal protected PoolingAsyncClientConnectionManager( final AsyncClientConnectionOperator connectionOperator, final PoolConcurrencyPolicy poolConcurrencyPolicy, final PoolReusePolicy poolReusePolicy, final TimeValue timeToLive) { this.connectionOperator = Args.notNull(connectionOperator, "Connection operator"); switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) { case STRICT: this.pool = new StrictConnPool( DEFAULT_MAX_CONNECTIONS_PER_ROUTE, DEFAULT_MAX_TOTAL_CONNECTIONS, timeToLive, poolReusePolicy, null) { @Override public void closeExpired() { enumAvailable(e -> closeIfExpired(e)); } }; break; case LAX: this.pool = new LaxConnPool( DEFAULT_MAX_CONNECTIONS_PER_ROUTE, timeToLive, poolReusePolicy, null) { @Override public void closeExpired() { enumAvailable(e -> closeIfExpired(e)); } }; break; default: throw new IllegalArgumentException("Unexpected PoolConcurrencyPolicy value: " + poolConcurrencyPolicy); } this.closed = new AtomicBoolean(false); } @Internal protected PoolingAsyncClientConnectionManager( final ManagedConnPool pool, final AsyncClientConnectionOperator connectionOperator) { this.connectionOperator = Args.notNull(connectionOperator, "Connection operator"); this.pool = Args.notNull(pool, "Connection pool"); this.closed = new AtomicBoolean(false); } @Override public void close() { close(CloseMode.GRACEFUL); } @Override public void close(final CloseMode closeMode) { if (this.closed.compareAndSet(false, true)) { if (LOG.isDebugEnabled()) { LOG.debug("Shutdown connection pool {}", closeMode); } this.pool.close(closeMode); LOG.debug("Connection pool shut down"); } } private InternalConnectionEndpoint cast(final AsyncConnectionEndpoint endpoint) { if (endpoint instanceof InternalConnectionEndpoint) { return (InternalConnectionEndpoint) endpoint; } throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass()); } private ConnectionConfig resolveConnectionConfig(final HttpRoute route) { final Resolver resolver = this.connectionConfigResolver; final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(route) : null; return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT; } private TlsConfig resolveTlsConfig(final HttpHost host, final Object attachment) { if (attachment instanceof TlsConfig) { return (TlsConfig) attachment; } final Resolver resolver = this.tlsConfigResolver; final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null; return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT; } @Override public Future lease( final String id, final HttpRoute route, final Object state, final Timeout requestTimeout, final FutureCallback callback) { if (LOG.isDebugEnabled()) { LOG.debug("{} endpoint lease request ({}) {}", id, requestTimeout, ConnPoolSupport.formatStats(route, state, pool)); } return new Future() { final ConnectionConfig connectionConfig = resolveConnectionConfig(route); final BasicFuture resultFuture = new BasicFuture<>(callback); final Future> leaseFuture = pool.lease( route, state, requestTimeout, new FutureCallback>() { @Override public void completed(final PoolEntry poolEntry) { if (poolEntry.hasConnection()) { final TimeValue timeToLive = connectionConfig.getTimeToLive(); if (TimeValue.isNonNegative(timeToLive)) { final Deadline deadline = Deadline.calculate(poolEntry.getCreated(), timeToLive); if (deadline.isExpired()) { poolEntry.discardConnection(CloseMode.GRACEFUL); } } } if (poolEntry.hasConnection()) { final ManagedAsyncClientConnection connection = poolEntry.getConnection(); final TimeValue timeValue = connectionConfig.getValidateAfterInactivity(); if (connection.isOpen() && TimeValue.isNonNegative(timeValue)) { final Deadline deadline = Deadline.calculate(poolEntry.getUpdated(), timeValue); if (deadline.isExpired()) { final ProtocolVersion protocolVersion = connection.getProtocolVersion(); if (protocolVersion != null && protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) { connection.submitCommand(new PingCommand(new BasicPingHandler(result -> { if (result == null || !result) { if (LOG.isDebugEnabled()) { LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(connection)); } poolEntry.discardConnection(CloseMode.GRACEFUL); } leaseCompleted(poolEntry); })), Command.Priority.IMMEDIATE); return; } else { if (LOG.isDebugEnabled()) { LOG.debug("{} connection {} is closed", id, ConnPoolSupport.getId(connection)); } poolEntry.discardConnection(CloseMode.IMMEDIATE); } } } } leaseCompleted(poolEntry); } void leaseCompleted(final PoolEntry poolEntry) { final ManagedAsyncClientConnection connection = poolEntry.getConnection(); if (connection != null) { connection.activate(); } if (LOG.isDebugEnabled()) { LOG.debug("{} endpoint leased {}", id, ConnPoolSupport.formatStats(route, state, pool)); } final AsyncConnectionEndpoint endpoint = new InternalConnectionEndpoint(poolEntry); if (LOG.isDebugEnabled()) { LOG.debug("{} acquired {}", id, ConnPoolSupport.getId(endpoint)); } resultFuture.completed(endpoint); } @Override public void failed(final Exception ex) { if (LOG.isDebugEnabled()) { LOG.debug("{} endpoint lease failed", id); } resultFuture.failed(ex); } @Override public void cancelled() { if (LOG.isDebugEnabled()) { LOG.debug("{} endpoint lease cancelled", id); } resultFuture.cancel(); } }); @Override public AsyncConnectionEndpoint get() throws InterruptedException, ExecutionException { return resultFuture.get(); } @Override public AsyncConnectionEndpoint get( final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return resultFuture.get(timeout, unit); } @Override public boolean cancel(final boolean mayInterruptIfRunning) { return leaseFuture.cancel(mayInterruptIfRunning); } @Override public boolean isDone() { return resultFuture.isDone(); } @Override public boolean isCancelled() { return resultFuture.isCancelled(); } }; } @Override public void release(final AsyncConnectionEndpoint endpoint, final Object state, final TimeValue keepAlive) { Args.notNull(endpoint, "Managed endpoint"); Args.notNull(keepAlive, "Keep-alive time"); final PoolEntry entry = cast(endpoint).detach(); if (entry == null) { return; } if (LOG.isDebugEnabled()) { LOG.debug("{} releasing endpoint", ConnPoolSupport.getId(endpoint)); } final ManagedAsyncClientConnection connection = entry.getConnection(); boolean reusable = connection != null && connection.isOpen(); try { if (reusable) { entry.updateState(state); entry.updateExpiry(keepAlive); connection.passivate(); if (LOG.isDebugEnabled()) { final String s; if (TimeValue.isPositive(keepAlive)) { s = "for " + keepAlive; } else { s = "indefinitely"; } LOG.debug("{} connection {} can be kept alive {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(connection), s); } } } catch (final RuntimeException ex) { reusable = false; throw ex; } finally { pool.release(entry, reusable); if (LOG.isDebugEnabled()) { LOG.debug("{} connection released {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.formatStats(entry.getRoute(), entry.getState(), pool)); } } } @Override public Future connect( final AsyncConnectionEndpoint endpoint, final ConnectionInitiator connectionInitiator, final Timeout timeout, final Object attachment, final HttpContext context, final FutureCallback callback) { Args.notNull(endpoint, "Endpoint"); Args.notNull(connectionInitiator, "Connection initiator"); final InternalConnectionEndpoint internalEndpoint = cast(endpoint); final ComplexFuture resultFuture = new ComplexFuture<>(callback); if (internalEndpoint.isConnected()) { resultFuture.completed(endpoint); return resultFuture; } final PoolEntry poolEntry = internalEndpoint.getPoolEntry(); final HttpRoute route = poolEntry.getRoute(); final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } final InetSocketAddress localAddress = route.getLocalSocketAddress(); final ConnectionConfig connectionConfig = resolveConnectionConfig(route); final TlsConfig tlsConfig = resolveTlsConfig(host, attachment); final Timeout connectTimeout = timeout != null ? timeout : connectionConfig.getConnectTimeout(); if (LOG.isDebugEnabled()) { LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout); } final Future connectFuture = connectionOperator.connect( connectionInitiator, host, localAddress, connectTimeout, route.isTunnelled() ? TlsConfig.copy(tlsConfig) .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1) .build() : tlsConfig, context, new FutureCallback() { @Override public void completed(final ManagedAsyncClientConnection connection) { try { if (LOG.isDebugEnabled()) { LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(connection)); } final ProtocolVersion protocolVersion = connection.getProtocolVersion(); final Timeout socketTimeout = connectionConfig.getSocketTimeout(); if (socketTimeout != null) { connection.setSocketTimeout(socketTimeout); } poolEntry.assignConnection(connection); resultFuture.completed(internalEndpoint); } catch (final RuntimeException ex) { resultFuture.failed(ex); } } @Override public void failed(final Exception ex) { resultFuture.failed(ex); } @Override public void cancelled() { resultFuture.cancel(); } }); resultFuture.setDependency(connectFuture); return resultFuture; } @Override public void upgrade( final AsyncConnectionEndpoint endpoint, final Object attachment, final HttpContext context, final FutureCallback callback) { Args.notNull(endpoint, "Managed endpoint"); final InternalConnectionEndpoint internalEndpoint = cast(endpoint); final PoolEntry poolEntry = internalEndpoint.getValidatedPoolEntry(); final HttpRoute route = poolEntry.getRoute(); final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost(); final TlsConfig tlsConfig = resolveTlsConfig(host, attachment); connectionOperator.upgrade( poolEntry.getConnection(), route.getTargetHost(), attachment != null ? attachment : tlsConfig, context, new CallbackContribution(callback) { @Override public void completed(final ManagedAsyncClientConnection connection) { if (LOG.isDebugEnabled()) { LOG.debug("{} upgraded {}", ConnPoolSupport.getId(internalEndpoint), ConnPoolSupport.getId(connection)); } final TlsDetails tlsDetails = connection.getTlsDetails(); if (tlsDetails != null && ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) { connection.switchProtocol(ApplicationProtocol.HTTP_2.id, new CallbackContribution(callback) { @Override public void completed(final ProtocolIOSession protocolIOSession) { if (callback != null) { callback.completed(endpoint); } } }); } else { if (callback != null) { callback.completed(endpoint); } } } }); } @Override public void upgrade(final AsyncConnectionEndpoint endpoint, final Object attachment, final HttpContext context) { upgrade(endpoint, attachment, context, null); } @Override public Set getRoutes() { return pool.getRoutes(); } @Override public void setMaxTotal(final int max) { pool.setMaxTotal(max); } @Override public int getMaxTotal() { return pool.getMaxTotal(); } @Override public void setDefaultMaxPerRoute(final int max) { pool.setDefaultMaxPerRoute(max); } @Override public int getDefaultMaxPerRoute() { return pool.getDefaultMaxPerRoute(); } @Override public void setMaxPerRoute(final HttpRoute route, final int max) { pool.setMaxPerRoute(route, max); } @Override public int getMaxPerRoute(final HttpRoute route) { return pool.getMaxPerRoute(route); } @Override public void closeIdle(final TimeValue idletime) { pool.closeIdle(idletime); } @Override public void closeExpired() { pool.closeExpired(); } @Override public PoolStats getTotalStats() { return pool.getTotalStats(); } @Override public PoolStats getStats(final HttpRoute route) { return pool.getStats(route); } /** * Sets the same {@link ConnectionConfig} for all routes * * @since 5.2 */ public void setDefaultConnectionConfig(final ConnectionConfig config) { this.connectionConfigResolver = (route) -> config; } /** * Sets {@link Resolver} of {@link ConnectionConfig} on a per route basis. * * @since 5.2 */ public void setConnectionConfigResolver(final Resolver connectionConfigResolver) { this.connectionConfigResolver = connectionConfigResolver; } /** * Sets the same {@link ConnectionConfig} for all hosts * * @since 5.2 */ public void setDefaultTlsConfig(final TlsConfig config) { this.tlsConfigResolver = (host) -> config; } /** * Sets {@link Resolver} of {@link TlsConfig} on a per host basis. * * @since 5.2 */ public void setTlsConfigResolver(final Resolver tlsConfigResolver) { this.tlsConfigResolver = tlsConfigResolver; } void closeIfExpired(final PoolEntry entry) { final long now = System.currentTimeMillis(); if (entry.getExpiryDeadline().isBefore(now)) { entry.discardConnection(CloseMode.GRACEFUL); } else { final ConnectionConfig connectionConfig = resolveConnectionConfig(entry.getRoute()); final TimeValue timeToLive = connectionConfig.getTimeToLive(); if (timeToLive != null && Deadline.calculate(entry.getCreated(), timeToLive).isBefore(now)) { entry.discardConnection(CloseMode.GRACEFUL); } } } /** * @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)} */ @Deprecated public TimeValue getValidateAfterInactivity() { return ConnectionConfig.DEFAULT.getValidateAfterInactivity(); } /** * Defines period of inactivity after which persistent connections must * be re-validated prior to being {@link #lease(String, HttpRoute, Object, Timeout, * FutureCallback)} leased} to the consumer. Negative values passed * to this method disable connection validation. This check helps detect connections * that have become stale (half-closed) while kept inactive in the pool. * * @deprecated Use {@link #setConnectionConfigResolver(Resolver)}. */ @Deprecated public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) { setDefaultConnectionConfig(ConnectionConfig.custom() .setValidateAfterInactivity(validateAfterInactivity) .build()); } private static final PrefixedIncrementingId INCREMENTING_ID = new PrefixedIncrementingId("ep-"); class InternalConnectionEndpoint extends AsyncConnectionEndpoint implements Identifiable { private final AtomicReference> poolEntryRef; private final String id; InternalConnectionEndpoint(final PoolEntry poolEntry) { this.poolEntryRef = new AtomicReference<>(poolEntry); this.id = INCREMENTING_ID.getNextId(); } @Override public String getId() { return id; } PoolEntry getPoolEntry() { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry == null) { throw new ConnectionShutdownException(); } return poolEntry; } PoolEntry getValidatedPoolEntry() { final PoolEntry poolEntry = getPoolEntry(); if (poolEntry.getConnection() == null) { throw new ConnectionShutdownException(); } return poolEntry; } PoolEntry detach() { return poolEntryRef.getAndSet(null); } @Override public void close(final CloseMode closeMode) { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry != null) { if (LOG.isDebugEnabled()) { LOG.debug("{} close {}", id, closeMode); } poolEntry.discardConnection(closeMode); } } @Override public boolean isConnected() { final PoolEntry poolEntry = poolEntryRef.get(); if (poolEntry == null) { return false; } final ManagedAsyncClientConnection connection = poolEntry.getConnection(); if (connection == null) { return false; } if (!connection.isOpen()) { poolEntry.discardConnection(CloseMode.IMMEDIATE); return false; } return true; } @Override public void setSocketTimeout(final Timeout timeout) { getValidatedPoolEntry().getConnection().setSocketTimeout(timeout); } @Override public void execute( final String exchangeId, final AsyncClientExchangeHandler exchangeHandler, final HandlerFactory pushHandlerFactory, final HttpContext context) { final ManagedAsyncClientConnection connection = getValidatedPoolEntry().getConnection(); if (LOG.isDebugEnabled()) { LOG.debug("{} executing exchange {} over {}", id, exchangeId, ConnPoolSupport.getId(connection)); } context.setProtocolVersion(connection.getProtocolVersion()); connection.submitCommand( new RequestExecutionCommand(exchangeHandler, pushHandlerFactory, context), Command.Priority.NORMAL); } } /** * Method that can be called to determine whether the connection manager has been shut down and * is closed or not. * * @return {@code true} if the connection manager has been shut down and is closed, otherwise * return {@code false}. */ boolean isClosed() { return this.closed.get(); } } PoolingAsyncClientConnectionManagerBuilder.java000066400000000000000000000230311434266521000435530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.nio; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.ssl.ConscryptClientTlsStrategy; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.core5.function.Resolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.util.ReflectionUtils; import org.apache.hc.core5.util.TimeValue; /** * Builder for {@link PoolingAsyncClientConnectionManager} instances. *

* When a particular component is not explicitly set 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()}. *

*
    *
  • ssl.TrustManagerFactory.algorithm
  • *
  • javax.net.ssl.trustStoreType
  • *
  • javax.net.ssl.trustStore
  • *
  • javax.net.ssl.trustStoreProvider
  • *
  • javax.net.ssl.trustStorePassword
  • *
  • ssl.KeyManagerFactory.algorithm
  • *
  • javax.net.ssl.keyStoreType
  • *
  • javax.net.ssl.keyStore
  • *
  • javax.net.ssl.keyStoreProvider
  • *
  • javax.net.ssl.keyStorePassword
  • *
  • https.protocols
  • *
  • https.cipherSuites
  • *
* * @since 5.0 */ public class PoolingAsyncClientConnectionManagerBuilder { private TlsStrategy tlsStrategy; private SchemePortResolver schemePortResolver; private DnsResolver dnsResolver; private PoolConcurrencyPolicy poolConcurrencyPolicy; private PoolReusePolicy poolReusePolicy; private boolean systemProperties; private int maxConnTotal; private int maxConnPerRoute; private Resolver connectionConfigResolver; private Resolver tlsConfigResolver; public static PoolingAsyncClientConnectionManagerBuilder create() { return new PoolingAsyncClientConnectionManagerBuilder(); } PoolingAsyncClientConnectionManagerBuilder() { super(); } /** * Assigns {@link TlsStrategy} instance for TLS connections. */ public final PoolingAsyncClientConnectionManagerBuilder setTlsStrategy( final TlsStrategy tlsStrategy) { this.tlsStrategy = tlsStrategy; return this; } /** * Assigns {@link DnsResolver} instance. */ public final PoolingAsyncClientConnectionManagerBuilder setDnsResolver(final DnsResolver dnsResolver) { this.dnsResolver = dnsResolver; return this; } /** * Assigns {@link SchemePortResolver} instance. */ public final PoolingAsyncClientConnectionManagerBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) { this.schemePortResolver = schemePortResolver; return this; } /** * Assigns {@link PoolConcurrencyPolicy} value. */ public final PoolingAsyncClientConnectionManagerBuilder setPoolConcurrencyPolicy(final PoolConcurrencyPolicy poolConcurrencyPolicy) { this.poolConcurrencyPolicy = poolConcurrencyPolicy; return this; } /** * Assigns {@link PoolReusePolicy} value. */ public final PoolingAsyncClientConnectionManagerBuilder setConnPoolPolicy(final PoolReusePolicy poolReusePolicy) { this.poolReusePolicy = poolReusePolicy; return this; } /** * Assigns maximum total connection value. */ public final PoolingAsyncClientConnectionManagerBuilder setMaxConnTotal(final int maxConnTotal) { this.maxConnTotal = maxConnTotal; return this; } /** * Assigns maximum connection per route value. */ public final PoolingAsyncClientConnectionManagerBuilder setMaxConnPerRoute(final int maxConnPerRoute) { this.maxConnPerRoute = maxConnPerRoute; return this; } /** * Assigns the same {@link ConnectionConfig} for all routes. * * @since 5.2 */ public final PoolingAsyncClientConnectionManagerBuilder setDefaultConnectionConfig(final ConnectionConfig config) { this.connectionConfigResolver = (route) -> config; return this; } /** * Assigns {@link Resolver} of {@link ConnectionConfig} on a per route basis. * * @since 5.2 */ public final PoolingAsyncClientConnectionManagerBuilder setConnectionConfigResolver( final Resolver connectionConfigResolver) { this.connectionConfigResolver = connectionConfigResolver; return this; } /** * Assigns the same {@link TlsConfig} for all hosts. * * @since 5.2 */ public final PoolingAsyncClientConnectionManagerBuilder setDefaultTlsConfig(final TlsConfig config) { this.tlsConfigResolver = (host) -> config; return this; } /** * Assigns {@link Resolver} of {@link TlsConfig} on a per host basis. * * @since 5.2 */ public final PoolingAsyncClientConnectionManagerBuilder setTlsConfigResolver( final Resolver tlsConfigResolver) { this.tlsConfigResolver = tlsConfigResolver; return this; } /** * Sets maximum time to live for persistent connections * * @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)} */ @Deprecated public final PoolingAsyncClientConnectionManagerBuilder setConnectionTimeToLive(final TimeValue timeToLive) { setDefaultConnectionConfig(ConnectionConfig.custom() .setTimeToLive(timeToLive) .build()); return this; } /** * Sets period after inactivity after which persistent * connections must be checked to ensure they are still valid. * * @deprecated Use {@link #setConnectionConfigResolver(Resolver)}. */ @Deprecated public final PoolingAsyncClientConnectionManagerBuilder setValidateAfterInactivity(final TimeValue validateAfterInactivity) { setDefaultConnectionConfig(ConnectionConfig.custom() .setValidateAfterInactivity(validateAfterInactivity) .build()); return this; } /** * Use system properties when creating and configuring default * implementations. */ public final PoolingAsyncClientConnectionManagerBuilder useSystemProperties() { this.systemProperties = true; return this; } public PoolingAsyncClientConnectionManager build() { final TlsStrategy tlsStrategyCopy; if (tlsStrategy != null) { tlsStrategyCopy = tlsStrategy; } else { if (ReflectionUtils.determineJRELevel() <= 8 && ConscryptClientTlsStrategy.isSupported()) { if (systemProperties) { tlsStrategyCopy = ConscryptClientTlsStrategy.getSystemDefault(); } else { tlsStrategyCopy = ConscryptClientTlsStrategy.getDefault(); } } else { if (systemProperties) { tlsStrategyCopy = DefaultClientTlsStrategy.getSystemDefault(); } else { tlsStrategyCopy = DefaultClientTlsStrategy.getDefault(); } } } final PoolingAsyncClientConnectionManager poolingmgr = new PoolingAsyncClientConnectionManager( RegistryBuilder.create() .register(URIScheme.HTTPS.getId(), tlsStrategyCopy) .build(), poolConcurrencyPolicy, poolReusePolicy, null, schemePortResolver, dnsResolver); poolingmgr.setConnectionConfigResolver(connectionConfigResolver); poolingmgr.setTlsConfigResolver(tlsConfigResolver); if (maxConnTotal > 0) { poolingmgr.setMaxTotal(maxConnTotal); } if (maxConnPerRoute > 0) { poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute); } return poolingmgr; } } package-info.java000066400000000000000000000024661434266521000355220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client connection management functions based the asynchronous * connection management APIs. */ package org.apache.hc.client5.http.impl.nio; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/000077500000000000000000000000001434266521000333045ustar00rootroot00000000000000BasicRouteDirector.java000066400000000000000000000142131434266521000376250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.client5.http.routing.HttpRouteDirector; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Basic {@link HttpRouteDirector} implementation. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class BasicRouteDirector implements HttpRouteDirector { /** * Singleton instance. * * @since 5.2 */ public static final BasicRouteDirector INSTANCE = new BasicRouteDirector(); /** * Provides the next step. * * @param plan the planned route * @param fact the currently established route, or * {@code null} if nothing is established * * @return one of the constants defined in this class, indicating * either the next step to perform, or success, or failure. * 0 is for success, a negative value for failure. */ @Override public int nextStep(final RouteInfo plan, final RouteInfo fact) { Args.notNull(plan, "Planned route"); int step = UNREACHABLE; if ((fact == null) || (fact.getHopCount() < 1)) { step = firstStep(plan); } else if (plan.getHopCount() > 1) { step = proxiedStep(plan, fact); } else { step = directStep(plan, fact); } return step; } // nextStep /** * Determines the first step to establish a route. * * @param plan the planned route * * @return the first step */ protected int firstStep(final RouteInfo plan) { return (plan.getHopCount() > 1) ? CONNECT_PROXY : CONNECT_TARGET; } /** * Determines the next step to establish a direct connection. * * @param plan the planned route * @param fact the currently established route * * @return one of the constants defined in this class, indicating * either the next step to perform, or success, or failure */ protected int directStep(final RouteInfo plan, final RouteInfo fact) { if (fact.getHopCount() > 1) { return UNREACHABLE; } if (!plan.getTargetHost().equals(fact.getTargetHost())) { return UNREACHABLE; // If the security is too low, we could now suggest to layer // a secure protocol on the direct connection. Layering on direct // connections has not been supported in HttpClient 3.x, we don't // consider it here until there is a real-life use case for it. } // Should we tolerate if security is better than planned? // (plan.isSecure() && !fact.isSecure()) if (plan.isSecure() != fact.isSecure()) { return UNREACHABLE; } // Local address has to match only if the plan specifies one. if ((plan.getLocalAddress() != null) && !plan.getLocalAddress().equals(fact.getLocalAddress()) ) { return UNREACHABLE; } return COMPLETE; } /** * Determines the next step to establish a connection via proxy. * * @param plan the planned route * @param fact the currently established route * * @return one of the constants defined in this class, indicating * either the next step to perform, or success, or failure */ protected int proxiedStep(final RouteInfo plan, final RouteInfo fact) { if (fact.getHopCount() <= 1) { return UNREACHABLE; } if (!plan.getTargetHost().equals(fact.getTargetHost())) { return UNREACHABLE; } final int phc = plan.getHopCount(); final int fhc = fact.getHopCount(); if (phc < fhc) { return UNREACHABLE; } for (int i=0; i fhc) { return TUNNEL_PROXY; // need to extend the proxy chain } // proxy chain and target are the same, check tunnelling and layering if ((fact.isTunnelled() && !plan.isTunnelled()) || (fact.isLayered() && !plan.isLayered())) { return UNREACHABLE; } if (plan.isTunnelled() && !fact.isTunnelled()) { return TUNNEL_TARGET; } if (plan.isLayered() && !fact.isLayered()) { return LAYER_PROTOCOL; } // tunnel and layering are the same, remains to check the security // Should we tolerate if security is better than planned? // (plan.isSecure() && !fact.isSecure()) if (plan.isSecure() != fact.isSecure()) { return UNREACHABLE; } return COMPLETE; } } DefaultProxyRoutePlanner.java000066400000000000000000000044671434266521000410700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Implementation of an {@link org.apache.hc.client5.http.routing.HttpRoutePlanner} * that routes requests through a default proxy. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultProxyRoutePlanner extends DefaultRoutePlanner { private final HttpHost proxy; public DefaultProxyRoutePlanner(final HttpHost proxy, final SchemePortResolver schemePortResolver) { super(schemePortResolver); this.proxy = Args.notNull(proxy, "Proxy host"); } public DefaultProxyRoutePlanner(final HttpHost proxy) { this(proxy, null); } @Override protected HttpHost determineProxy( final HttpHost target, final HttpContext context) throws HttpException { return proxy; } } DefaultRoutePlanner.java000066400000000000000000000102561434266521000400170ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import java.net.InetAddress; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.protocol.HttpContext; /** * Default implementation of an {@link HttpRoutePlanner}. It will not make use of * any Java system properties, nor of system or browser proxy settings. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultRoutePlanner implements HttpRoutePlanner { private final SchemePortResolver schemePortResolver; public DefaultRoutePlanner(final SchemePortResolver schemePortResolver) { super(); this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE; } @Override public final HttpRoute determineRoute(final HttpHost host, final HttpContext context) throws HttpException { if (host == null) { throw new ProtocolException("Target host is not specified"); } final HttpClientContext clientContext = HttpClientContext.adapt(context); final RequestConfig config = clientContext.getRequestConfig(); @SuppressWarnings("deprecation") HttpHost proxy = config.getProxy(); if (proxy == null) { proxy = determineProxy(host, context); } final HttpHost target = RoutingSupport.normalize(host, schemePortResolver); if (target.getPort() < 0) { throw new ProtocolException("Unroutable protocol scheme: " + target); } final boolean secure = target.getSchemeName().equalsIgnoreCase(URIScheme.HTTPS.getId()); if (proxy == null) { return new HttpRoute(target, determineLocalAddress(target, context), secure); } return new HttpRoute(target, determineLocalAddress(proxy, context), proxy, secure); } /** * This implementation returns null. * * @throws HttpException may be thrown if overridden */ protected HttpHost determineProxy( final HttpHost target, final HttpContext context) throws HttpException { return null; } /** * This implementation returns null. * * @throws HttpException may be thrown if overridden */ protected InetAddress determineLocalAddress( final HttpHost firstHop, final HttpContext context) throws HttpException { return null; } } SystemDefaultRoutePlanner.java000066400000000000000000000116621434266521000412260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.ProxySelector; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; /** * {@link org.apache.hc.client5.http.routing.HttpRoutePlanner} implementation * based on {@link ProxySelector}. By default, this class will pick up * the proxy settings of the JVM, either from system properties * or from the browser running the application. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class SystemDefaultRoutePlanner extends DefaultRoutePlanner { private final ProxySelector proxySelector; /** * @param proxySelector the proxy selector, or {@code null} for the system default */ public SystemDefaultRoutePlanner( final SchemePortResolver schemePortResolver, final ProxySelector proxySelector) { super(schemePortResolver); this.proxySelector = proxySelector; } /** * @param proxySelector the proxy selector, or {@code null} for the system default */ public SystemDefaultRoutePlanner(final ProxySelector proxySelector) { this(null, proxySelector); } @Override protected HttpHost determineProxy(final HttpHost target, final HttpContext context) throws HttpException { final URI targetURI; try { targetURI = new URI(target.toURI()); } catch (final URISyntaxException ex) { throw new HttpException("Cannot convert host to URI: " + target, ex); } ProxySelector proxySelectorInstance = this.proxySelector; if (proxySelectorInstance == null) { proxySelectorInstance = ProxySelector.getDefault(); } if (proxySelectorInstance == null) { //The proxy selector can be "unset", so we must be able to deal with a null selector return null; } final List proxies = proxySelectorInstance.select(targetURI); final Proxy p = chooseProxy(proxies); HttpHost result = null; if (p.type() == Proxy.Type.HTTP) { // convert the socket address to an HttpHost if (!(p.address() instanceof InetSocketAddress)) { throw new HttpException("Unable to handle non-Inet proxy address: " + p.address()); } final InetSocketAddress isa = (InetSocketAddress) p.address(); // assume default scheme (http) result = new HttpHost(null, isa.getAddress(), isa.getHostString(), isa.getPort()); } return result; } private Proxy chooseProxy(final List proxies) { Proxy result = null; // check the list for one we can use for (int i=0; (result == null) && (i < proxies.size()); i++) { final Proxy p = proxies.get(i); switch (p.type()) { case DIRECT: case HTTP: result = p; break; case SOCKS: // SOCKS hosts are not handled on the route level. // The socket may make use of the SOCKS host though. break; } } if (result == null) { //@@@ log as warning or info that only a socks proxy is available? // result can only be null if all proxies are socks proxies // socks proxies are not handled on the route planning level result = Proxy.NO_PROXY; } return result; } } package-info.java000066400000000000000000000024251434266521000364170ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Standard client connection routing API implementations. */ package org.apache.hc.client5.http.impl.routing; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/io/000077500000000000000000000000001434266521000312635ustar00rootroot00000000000000ConnectionEndpoint.java000066400000000000000000000057431434266521000356600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.io; import java.io.IOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Timeout; /** * Client endpoint leased from a connection manager. Client points can be used * to execute HTTP requests. *

* Once the endpoint is no longer needed it MUST be released with {@link #close(org.apache.hc.core5.io.CloseMode)} )}. *

* * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class ConnectionEndpoint implements ModalCloseable { /** * Executes HTTP request using the provided request executor. *

* Once the endpoint is no longer needed it MUST be released with {@link #close(org.apache.hc.core5.io.CloseMode)}. *

* * @param id unique operation ID or {@code null}. * @param request the request message. * @param executor the request executor. * @param context the execution context. */ public abstract ClassicHttpResponse execute( String id, ClassicHttpRequest request, HttpRequestExecutor executor, HttpContext context) throws IOException, HttpException; /** * Determines if the connection to the remote endpoint is still open and valid. */ public abstract boolean isConnected(); /** * Sets the socket timeout value. * * @param timeout timeout value */ public abstract void setSocketTimeout(Timeout timeout); } HttpClientConnectionManager.java000066400000000000000000000112461434266521000374440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.io; import java.io.IOException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Represents a manager of persistent client connections. *

* 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. *

*

* 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.3 */ public interface HttpClientConnectionManager extends ModalCloseable { /** * Returns a {@link LeaseRequest} object which can be used to obtain * a {@link ConnectionEndpoint} to cancel the request by calling * {@link LeaseRequest#cancel()}. *

* Please note that newly allocated endpoints can be leased * {@link ConnectionEndpoint#isConnected() disconnected}. The consumer of the endpoint * is responsible for fully establishing the route to the endpoint target * by calling {@link #connect(ConnectionEndpoint, TimeValue, HttpContext)} * in order to connect directly to the target or to the first proxy hop, * and optionally calling {@link #upgrade(ConnectionEndpoint, HttpContext)} method * to upgrade the underlying transport to Transport Layer Security after having * executed a {@code CONNECT} method to all intermediate proxy hops. * * @param id unique operation ID or {@code null}. * @param route HTTP route of the requested connection. * @param requestTimeout lease request timeout. * @param state expected state of the connection or {@code null} * if the connection is not expected to carry any state. * @since 5.0 */ LeaseRequest lease(String id, HttpRoute route, Timeout requestTimeout, Object state); /** * Releases the endpoint 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 endpoint the managed endpoint. * @param newState the new connection state of {@code null} if state-less. * @param validDuration the duration of time this connection is valid for reuse. */ void release(ConnectionEndpoint endpoint, Object newState, TimeValue validDuration); /** * Connects the endpoint to the initial hop (connection target in case * of a direct route or to the first proxy hop in case of a route via a proxy * or multiple proxies). * * @param endpoint the managed endpoint. * @param connectTimeout connect timeout. * @param context the actual HTTP context. */ void connect(ConnectionEndpoint endpoint, TimeValue connectTimeout, HttpContext context) throws IOException; /** * Upgrades transport security of the given endpoint by using the TLS security protocol. * * @param endpoint the managed endpoint. * @param context the actual HTTP context. */ void upgrade(ConnectionEndpoint endpoint, HttpContext context) throws IOException; } HttpClientConnectionOperator.java000066400000000000000000000106311434266521000376620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.io; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Connection operator that performs connection connect and upgrade operations. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public interface HttpClientConnectionOperator { /** * Connect the given managed connection to the remote endpoint. * * @param conn the managed connection. * @param host the address of the opposite endpoint. * @param localAddress the address of the local endpoint. * @param connectTimeout the timeout of the connect operation. * @param socketConfig the socket configuration. * @param context the execution context. */ void connect( ManagedHttpClientConnection conn, HttpHost host, InetSocketAddress localAddress, TimeValue connectTimeout, SocketConfig socketConfig, HttpContext context) throws IOException; /** * Connect the given managed connection to the remote endpoint. * * @param conn the managed connection. * @param host the address of the opposite endpoint. * @param localAddress the address of the local endpoint. * @param connectTimeout the timeout of the connect operation. * @param socketConfig the socket configuration. * @param attachment connect request attachment. * @param context the execution context. * * @since 5.2 */ default void connect( ManagedHttpClientConnection conn, HttpHost host, InetSocketAddress localAddress, Timeout connectTimeout, SocketConfig socketConfig, Object attachment, HttpContext context) throws IOException { connect(conn, host, localAddress, connectTimeout, socketConfig, context); } /** * Upgrades transport security of the given managed connection * by using the TLS security protocol. * * @param conn the managed connection. * @param host the address of the opposite endpoint with TLS security. * @param context the execution context. */ void upgrade( ManagedHttpClientConnection conn, HttpHost host, HttpContext context) throws IOException; /** * Upgrades transport security of the given managed connection * by using the TLS security protocol. * * @param conn the managed connection. * @param host the address of the opposite endpoint with TLS security. * @param attachment connect request attachment. * @param context the execution context. * * @since 5.2 */ default void upgrade( ManagedHttpClientConnection conn, HttpHost host, Object attachment, HttpContext context) throws IOException { upgrade(conn, host, context); } } LeaseRequest.java000066400000000000000000000046711434266521000344610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.io; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.apache.hc.core5.concurrent.Cancellable; import org.apache.hc.core5.util.Timeout; /** * Represents a request for a {@link ConnectionEndpoint} whose life cycle * is managed by a connection manager. * * @since 5.0 */ public interface LeaseRequest extends Cancellable { /** * Returns {@link ConnectionEndpoint} within a given time. * This method will block until a connection becomes available, * the timeout expires, or the connection manager is shut down. * Timeouts are handled with millisecond precision. * * If {@link #cancel()} is called while this is blocking or * before this began, an {@link InterruptedException} will * be thrown. * * @param timeout the operation timeout. * * @return a connection that can be used to communicate * along the given route * * @throws TimeoutException * in case of a timeout * @throws InterruptedException * if the calling thread is interrupted while waiting */ ConnectionEndpoint get(Timeout timeout) throws InterruptedException, ExecutionException, TimeoutException; }ManagedHttpClientConnection.java000066400000000000000000000054151434266521000374270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.io; import java.io.IOException; import java.net.Socket; import javax.net.ssl.SSLSession; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.http.io.HttpClientConnection; /** * Represents a managed connection whose state and life cycle is managed by * a connection manager. This interface extends {@link HttpClientConnection} * with methods to bind the connection to an arbitrary socket and * to obtain SSL session details. * * @since 4.3 */ @Internal public interface ManagedHttpClientConnection extends HttpClientConnection { /** * Binds this connection to the given socket. The connection * is considered open if it is bound and the underlying socket * is connection to a remote host. * * @param socket the socket to bind the connection to. */ void bind(Socket socket) throws IOException; /** * Returns the underlying socket. */ Socket getSocket(); /** * Obtains the SSL session of the underlying connection, if any. * If this connection is open, and the underlying socket is an * {@link javax.net.ssl.SSLSocket SSLSocket}, the SSL session of * that socket is obtained. This is a potentially blocking operation. * * @return the underlying SSL session if available, * {@code null} otherwise */ @Override SSLSession getSSLSession(); /** * Puts the connection into idle mode. * * @since 5.0 */ void passivate(); /** * Restores the connection from idle mode. * * @since 5.0 */ void activate(); } package-info.java000066400000000000000000000024311434266521000343730ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Connection management APIs based on the classic (blocking) I/O model. */ package org.apache.hc.client5.http.io; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/nio/000077500000000000000000000000001434266521000314415ustar00rootroot00000000000000AsyncClientConnectionManager.java000066400000000000000000000143631434266521000377630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.nio; import java.util.concurrent.Future; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Represents a manager of persistent non-blocking client connections. *

* 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. *

*

* 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 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public interface AsyncClientConnectionManager extends ModalCloseable { /** * Returns a {@link Future} object which can be used to obtain * an {@link AsyncConnectionEndpoint} or to cancel the request by calling * {@link Future#cancel(boolean)}. *

* Please note that newly allocated endpoints can be leased * {@link AsyncConnectionEndpoint#isConnected() disconnected}. The consumer * of the endpoint is responsible for fully establishing the route to * the endpoint target by calling {@link #connect(AsyncConnectionEndpoint, * ConnectionInitiator, Timeout, Object, HttpContext, FutureCallback)} * in order to connect directly to the target or to the first proxy hop, * and optionally calling {@link #upgrade(AsyncConnectionEndpoint, Object, HttpContext)} * method to upgrade the underlying transport to Transport Layer Security * after having executed a {@code CONNECT} method to all intermediate * proxy hops. * * @param id unique operation ID or {@code null}. * @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 requestTimeout lease request timeout. * @param callback result callback. */ Future lease( String id, HttpRoute route, Object state, Timeout requestTimeout, FutureCallback callback); /** * Releases the endpoint 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 endpoint the managed endpoint. * @param newState the new connection state of {@code null} if state-less. * @param validDuration the duration of time this connection is valid for reuse. */ void release(AsyncConnectionEndpoint endpoint, Object newState, TimeValue validDuration); /** * Connects the endpoint to the initial hop (connection target in case * of a direct route or to the first proxy hop in case of a route via a proxy * or multiple proxies). * * @param endpoint the managed endpoint. * @param connectTimeout connect timeout. * @param context the actual HTTP context. * @param attachment connect request attachment. * @param callback result callback. */ Future connect( AsyncConnectionEndpoint endpoint, ConnectionInitiator connectionInitiator, Timeout connectTimeout, Object attachment, HttpContext context, FutureCallback callback); /** * Upgrades transport security of the given endpoint by using the TLS security protocol. * * @param endpoint the managed endpoint. * @param attachment the attachment the upgrade attachment object. * @param context the actual HTTP context. */ void upgrade( AsyncConnectionEndpoint endpoint, Object attachment, HttpContext context); /** * Upgrades transport security of the given endpoint by using the TLS security protocol. * * @param endpoint the managed endpoint. * @param attachment the attachment the upgrade attachment object. * @param context the actual HTTP context. * @param callback result callback. * * @since 5.2 */ default void upgrade( AsyncConnectionEndpoint endpoint, Object attachment, HttpContext context, FutureCallback callback) { upgrade(endpoint, attachment, context); if (callback != null) { callback.completed(endpoint); } } } AsyncClientConnectionOperator.java000066400000000000000000000134071434266521000402020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.nio; import java.net.SocketAddress; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.reactor.ConnectionInitiator; import org.apache.hc.core5.util.Timeout; /** * Connection operator that performs connection connect and upgrade operations. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) @Internal public interface AsyncClientConnectionOperator { /** * Initiates operation to create a connection to the remote endpoint using * the provided {@link ConnectionInitiator}. * * @param connectionInitiator the connection initiator. * @param host the address of the opposite endpoint. * @param localAddress the address of the local endpoint. * @param connectTimeout the timeout of the connect operation. * @param attachment the attachment, which can be any object representing custom parameter * of the operation. * @param callback the future result callback. */ Future connect( ConnectionInitiator connectionInitiator, HttpHost host, SocketAddress localAddress, Timeout connectTimeout, Object attachment, FutureCallback callback); /** * Initiates operation to create a connection to the remote endpoint using * the provided {@link ConnectionInitiator}. * * @param connectionInitiator the connection initiator. * @param host the address of the opposite endpoint. * @param localAddress the address of the local endpoint. * @param connectTimeout the timeout of the connect operation. * @param attachment the attachment, which can be any object representing custom parameter * of the operation. * @param context the execution context. * @param callback the future result callback. * @since 5.2 */ default Future connect( ConnectionInitiator connectionInitiator, HttpHost host, SocketAddress localAddress, Timeout connectTimeout, Object attachment, HttpContext context, FutureCallback callback) { return connect(connectionInitiator, host, localAddress, connectTimeout, attachment, callback); } /** * Upgrades transport security of the given managed connection * by using the TLS security protocol. * * @param conn the managed connection. * @param host the address of the opposite endpoint with TLS security. * @param attachment the attachment, which can be any object representing custom parameter * of the operation. */ void upgrade(ManagedAsyncClientConnection conn, HttpHost host, Object attachment); /** * Upgrades transport security of the given managed connection * by using the TLS security protocol. * * @param conn the managed connection. * @param host the address of the opposite endpoint with TLS security. * @param attachment the attachment, which can be any object representing custom parameter * of the operation. * @param context the execution context. * @param callback the future result callback. * @since 5.2 */ default void upgrade( ManagedAsyncClientConnection conn, HttpHost host, Object attachment, HttpContext context, FutureCallback callback) { upgrade(conn, host, attachment, context); if (callback != null) { callback.completed(conn); } } /** * Upgrades transport security of the given managed connection * by using the TLS security protocol. * * @param conn the managed connection. * @param host the address of the opposite endpoint with TLS security. * @param attachment the attachment, which can be any object representing custom parameter * of the operation. * @param context the execution context. * @since 5.2 */ default void upgrade(ManagedAsyncClientConnection conn, HttpHost host, Object attachment, HttpContext context) { upgrade(conn, host, attachment); } } AsyncConnectionEndpoint.java000066400000000000000000000173061434266521000370320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.nio; import java.io.IOException; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.BasicFuture; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.AsyncPushConsumer; import org.apache.hc.core5.http.nio.AsyncRequestProducer; import org.apache.hc.core5.http.nio.AsyncResponseConsumer; import org.apache.hc.core5.http.nio.HandlerFactory; import org.apache.hc.core5.http.nio.support.BasicClientExchangeHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.io.ModalCloseable; import org.apache.hc.core5.util.Timeout; /** * Client connection endpoint that can be used to execute message exchanges. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.SAFE) public abstract class AsyncConnectionEndpoint implements ModalCloseable { /** * Initiates a message exchange using the given handler. * * @param id unique operation ID or {@code null}. * @param exchangeHandler the message exchange handler. * @param pushHandlerFactory the push handler factory. * @param context the execution context. */ public abstract void execute( String id, AsyncClientExchangeHandler exchangeHandler, HandlerFactory pushHandlerFactory, HttpContext context); /** * Determines if the connection to the remote endpoint is still open and valid. */ public abstract boolean isConnected(); /** * Sets socket timeout. * * @param timeout the socket timeout. */ public abstract void setSocketTimeout(Timeout timeout); @Override public final void close() throws IOException { close(CloseMode.GRACEFUL); } /** * Initiates a message exchange using the given handler. * * @param id unique operation ID or {@code null}. * @param exchangeHandler the message exchange handler. * @param context the execution context. */ public void execute( final String id, final AsyncClientExchangeHandler exchangeHandler, final HttpContext context) { execute(id, exchangeHandler, null, context); } /** * Initiates message exchange using the given request producer and response consumer. * * @param id unique operation ID or {@code null}. * @param requestProducer the request producer. * @param responseConsumer the response consumer. * @param pushHandlerFactory the push handler factory. * @param context the execution context. * @param callback the result callback. * @param the result representation. * @return the result future. */ public final Future execute( final String id, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final HttpContext context, final FutureCallback callback) { final BasicFuture future = new BasicFuture<>(callback); execute(id, new BasicClientExchangeHandler<>(requestProducer, responseConsumer, new FutureCallback() { @Override public void completed(final T result) { future.completed(result); } @Override public void failed(final Exception ex) { future.failed(ex); } @Override public void cancelled() { future.cancel(); } }), pushHandlerFactory, context != null ? context : HttpCoreContext.create()); return future; } /** * Initiates message exchange using the given request producer and response consumer. * * @param id unique operation ID or {@code null}. * @param requestProducer the request producer. * @param responseConsumer the response consumer. * @param context the execution context. * @param callback the result callback. * @param the result representation. * @return the result future. */ public final Future execute( final String id, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HttpContext context, final FutureCallback callback) { return execute(id, requestProducer, responseConsumer, null, context, callback); } /** * Initiates message exchange using the given request producer and response consumer. * * @param id unique operation ID or {@code null}. * @param requestProducer the request producer. * @param responseConsumer the response consumer. * @param pushHandlerFactory the push handler factory. * @param callback the result callback. * @param the result representation. * @return the result future. */ public final Future execute( final String id, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final HandlerFactory pushHandlerFactory, final FutureCallback callback) { return execute(id, requestProducer, responseConsumer, pushHandlerFactory, null, callback); } /** * Initiates message exchange using the given request producer and response consumer. * * @param id unique operation ID or {@code null}. * @param requestProducer the request producer. * @param responseConsumer the response consumer. * @param callback the result callback. * @param the result representation. * @return the result future. */ public final Future execute( final String id, final AsyncRequestProducer requestProducer, final AsyncResponseConsumer responseConsumer, final FutureCallback callback) { return execute(id, requestProducer, responseConsumer, null, null, callback); } } ManagedAsyncClientConnection.java000066400000000000000000000052501434266521000377400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.nio; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.ProtocolIOSession; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; /** * Represents a managed asynchronous connection whose state and life cycle * is managed by a connection manager. * * @since 5.0 */ @Internal public interface ManagedAsyncClientConnection extends HttpConnection, TransportSecurityLayer { /** * Submits the given command for execution. * * @param command the command to be executed. * @param priority the command priority. */ void submitCommand(Command command, Command.Priority priority); /** * Puts the connection into idle mode. */ void passivate(); /** * Restores the connection from idle mode. */ void activate(); /** * Switches this I/O session to the application protocol with the given ID. * @param protocolId the application protocol ID * @param callback the result callback * @throws UnsupportedOperationException if application protocol switch * is not supported. * * @since 5.2 */ default void switchProtocol(String protocolId, FutureCallback callback) throws UnsupportedOperationException { throw new UnsupportedOperationException("Protocol switch not supported"); } } package-info.java000066400000000000000000000024241434266521000345530ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/nio/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Connection management APIs based on the asynchronous I/O model. */ package org.apache.hc.client5.http.nio; package-info.java000066400000000000000000000026041434266521000337660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client component APIs common to all client transports * such as connection route information and resolution * as well as common HTTP method definitions and * exception classes. */ package org.apache.hc.client5.http; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/000077500000000000000000000000001434266521000325155ustar00rootroot00000000000000HttpClientContext.java000066400000000000000000000236441434266521000367350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.util.HashMap; import java.util.Map; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.util.Args; /** * Adaptor class that provides convenience type safe setters and getters * for common {@link HttpContext} attributes used in the course * of HTTP request execution. * * @since 4.3 */ public class HttpClientContext extends HttpCoreContext { /** * Attribute name of a {@link RouteInfo} * object that represents the actual connection route. */ public static final String HTTP_ROUTE = "http.route"; /** * Attribute name of a {@link RedirectLocations} object that represents a collection of all * redirect locations received in the process of request execution. */ public static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations"; /** * Attribute name of a {@link org.apache.hc.core5.http.config.Lookup} object that represents * the actual {@link CookieSpecFactory} registry. */ public static final String COOKIESPEC_REGISTRY = "http.cookiespec-registry"; /** * Attribute name of a {@link org.apache.hc.client5.http.cookie.CookieSpec} * object that represents the actual cookie specification. */ public static final String COOKIE_SPEC = "http.cookie-spec"; /** * Attribute name of a {@link org.apache.hc.client5.http.cookie.CookieOrigin} * object that represents the actual details of the origin server. */ public static final String COOKIE_ORIGIN = "http.cookie-origin"; /** * Attribute name of a {@link CookieStore} * object that represents the actual cookie store. */ public static final String COOKIE_STORE = "http.cookie-store"; /** * Attribute name of a {@link CredentialsProvider} * object that represents the actual credentials provider. */ public static final String CREDS_PROVIDER = "http.auth.credentials-provider"; /** * Attribute name of a {@link AuthCache} object * that represents the auth scheme cache. */ public static final String AUTH_CACHE = "http.auth.auth-cache"; /** * Attribute name of a map containing actual {@link AuthExchange}s keyed by their respective * {@link org.apache.hc.core5.http.HttpHost}. */ public static final String AUTH_EXCHANGE_MAP = "http.auth.exchanges"; /** * Attribute name of a {@link java.lang.Object} object that represents * the actual user identity such as user {@link java.security.Principal}. */ public static final String USER_TOKEN = "http.user-token"; /** * Attribute name of a {@link org.apache.hc.core5.http.config.Lookup} object that represents * the actual {@link AuthSchemeFactory} registry. */ public static final String AUTHSCHEME_REGISTRY = "http.authscheme-registry"; /** * Attribute name of a {@link org.apache.hc.client5.http.config.RequestConfig} object that * represents the actual request configuration. */ public static final String REQUEST_CONFIG = "http.request-config"; /** * Attribute name of a {@link java.lang.String} object that represents the ID of the * current message exchange. */ public static final String EXCHANGE_ID = "http.exchange-id"; public static HttpClientContext adapt(final HttpContext context) { Args.notNull(context, "HTTP context"); if (context instanceof HttpClientContext) { return (HttpClientContext) context; } return new HttpClientContext(context); } public static HttpClientContext create() { return new HttpClientContext(new BasicHttpContext()); } public HttpClientContext(final HttpContext context) { super(context); } public HttpClientContext() { super(); } public RouteInfo getHttpRoute() { return getAttribute(HTTP_ROUTE, HttpRoute.class); } public RedirectLocations getRedirectLocations() { return getAttribute(REDIRECT_LOCATIONS, RedirectLocations.class); } public CookieStore getCookieStore() { return getAttribute(COOKIE_STORE, CookieStore.class); } public void setCookieStore(final CookieStore cookieStore) { setAttribute(COOKIE_STORE, cookieStore); } public CookieSpec getCookieSpec() { return getAttribute(COOKIE_SPEC, CookieSpec.class); } public CookieOrigin getCookieOrigin() { return getAttribute(COOKIE_ORIGIN, CookieOrigin.class); } @SuppressWarnings("unchecked") private Lookup getLookup(final String name) { return (Lookup) getAttribute(name, Lookup.class); } public Lookup getCookieSpecRegistry() { return getLookup(COOKIESPEC_REGISTRY); } public void setCookieSpecRegistry(final Lookup lookup) { setAttribute(COOKIESPEC_REGISTRY, lookup); } public Lookup getAuthSchemeRegistry() { return getLookup(AUTHSCHEME_REGISTRY); } public void setAuthSchemeRegistry(final Lookup lookup) { setAttribute(AUTHSCHEME_REGISTRY, lookup); } public CredentialsProvider getCredentialsProvider() { return getAttribute(CREDS_PROVIDER, CredentialsProvider.class); } public void setCredentialsProvider(final CredentialsProvider credentialsProvider) { setAttribute(CREDS_PROVIDER, credentialsProvider); } public AuthCache getAuthCache() { return getAttribute(AUTH_CACHE, AuthCache.class); } public void setAuthCache(final AuthCache authCache) { setAttribute(AUTH_CACHE, authCache); } /** * @since 5.0 */ @SuppressWarnings("unchecked") public Map getAuthExchanges() { Map map = (Map) getAttribute(AUTH_EXCHANGE_MAP); if (map == null) { map = new HashMap<>(); setAttribute(AUTH_EXCHANGE_MAP, map); } return map; } /** * @since 5.0 */ public AuthExchange getAuthExchange(final HttpHost host) { final Map authExchangeMap = getAuthExchanges(); AuthExchange authExchange = authExchangeMap.get(host); if (authExchange == null) { authExchange = new AuthExchange(); authExchangeMap.put(host, authExchange); } return authExchange; } /** * @since 5.0 */ public void setAuthExchange(final HttpHost host, final AuthExchange authExchange) { final Map authExchangeMap = getAuthExchanges(); authExchangeMap.put(host, authExchange); } /** * @since 5.0 */ public void resetAuthExchange(final HttpHost host, final AuthScheme authScheme) { final AuthExchange authExchange = new AuthExchange(); authExchange.select(authScheme); final Map authExchangeMap = getAuthExchanges(); authExchangeMap.put(host, authExchange); } public T getUserToken(final Class clazz) { return getAttribute(USER_TOKEN, clazz); } public Object getUserToken() { return getAttribute(USER_TOKEN); } public void setUserToken(final Object obj) { setAttribute(USER_TOKEN, obj); } public RequestConfig getRequestConfig() { final RequestConfig config = getAttribute(REQUEST_CONFIG, RequestConfig.class); return config != null ? config : RequestConfig.DEFAULT; } public void setRequestConfig(final RequestConfig config) { setAttribute(REQUEST_CONFIG, config); } /** * @since 5.1 */ public String getExchangeId() { return getAttribute(EXCHANGE_ID, String.class); } /** * @since 5.1 */ public void setExchangeId(final String id) { setAttribute(EXCHANGE_ID, id); } } RedirectLocations.java000066400000000000000000000061301434266521000367160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * This class represents a collection of {@link java.net.URI}s used * as redirect locations. * * @since 4.0 */ public final class RedirectLocations { private final Set unique; private final List all; public RedirectLocations() { super(); this.unique = new HashSet<>(); this.all = new ArrayList<>(); } /** * Test if the URI is present in the collection. */ public boolean contains(final URI uri) { return this.unique.contains(uri); } /** * Adds a new URI to the collection. */ public void add(final URI uri) { this.unique.add(uri); this.all.add(uri); } /** * Returns all redirect {@link URI}s in the order they were added to the collection. * * @return list of all URIs * * @since 4.1 */ public List getAll() { return new ArrayList<>(this.all); } /** * Returns the URI at the specified position in this list. * * @param index * index of the location to return * @return the URI at the specified position in this list * @throws IndexOutOfBoundsException * if the index is out of range ( * {@code index < 0 || index >= size()}) * @since 4.3 */ public URI get(final int index) { return this.all.get(index); } /** * Returns the number of elements in this list. If this list contains more * than {@code Integer.MAX_VALUE} elements, returns * {@code Integer.MAX_VALUE}. * * @return the number of elements in this list * @since 4.3 */ public int size() { return this.all.size(); } public void clear() { unique.clear(); all.clear(); } } RedirectStrategy.java000066400000000000000000000052671434266521000365770ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.net.URI; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.protocol.HttpContext; /** * A strategy for determining if an HTTP request should be redirected to * a new location in response to an HTTP response received from the target * server. *

* 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.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface RedirectStrategy { /** * Determines if a request should be redirected to a new location * given the response from the target server. * * @param request the executed request * @param response the response received from the target server * @param context the context for the request execution * @return {@code true} if the request should be redirected, {@code false} * otherwise */ boolean isRedirected( HttpRequest request, HttpResponse response, HttpContext context) throws HttpException; URI getLocationURI( final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException; } RequestAddCookies.java000066400000000000000000000170041434266521000366610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.List; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request interceptor that matches cookies available in the current * {@link CookieStore} to the request being executed and generates * corresponding {@code Cookie} request headers. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class RequestAddCookies implements HttpRequestInterceptor { /** * Singleton instance. * * @since 5.2 */ public static final RequestAddCookies INSTANCE = new RequestAddCookies(); private static final Logger LOG = LoggerFactory.getLogger(RequestAddCookies.class); public RequestAddCookies() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); Args.notNull(context, "HTTP context"); final String method = request.getMethod(); if (Method.CONNECT.isSame(method) || Method.TRACE.isSame(method)) { return; } final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); // Obtain cookie store final CookieStore cookieStore = clientContext.getCookieStore(); if (cookieStore == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie store not specified in HTTP context", exchangeId); } return; } // Obtain the registry of cookie specs final Lookup registry = clientContext.getCookieSpecRegistry(); if (registry == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} CookieSpec registry not specified in HTTP context", exchangeId); } return; } // Obtain the route (required) final RouteInfo route = clientContext.getHttpRoute(); if (route == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Connection route not set in the context", exchangeId); } return; } final RequestConfig config = clientContext.getRequestConfig(); String cookieSpecName = config.getCookieSpec(); if (cookieSpecName == null) { cookieSpecName = StandardCookieSpec.STRICT; } if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie spec selected: {}", exchangeId, cookieSpecName); } final URIAuthority authority = request.getAuthority(); String path = request.getPath(); if (TextUtils.isEmpty(path)) { path = "/"; } String hostName = authority != null ? authority.getHostName() : null; if (hostName == null) { hostName = route.getTargetHost().getHostName(); } int port = authority != null ? authority.getPort() : -1; if (port < 0) { port = route.getTargetHost().getPort(); } final CookieOrigin cookieOrigin = new CookieOrigin(hostName, port, path, route.isSecure()); // Get an instance of the selected cookie policy final CookieSpecFactory factory = registry.lookup(cookieSpecName); if (factory == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Unsupported cookie spec: {}", exchangeId, cookieSpecName); } return; } final CookieSpec cookieSpec = factory.create(clientContext); // Get all cookies available in the HTTP state final List cookies = cookieStore.getCookies(); // Find cookies matching the given origin final List matchedCookies = new ArrayList<>(); final Instant now = Instant.now(); boolean expired = false; for (final Cookie cookie : cookies) { if (!cookie.isExpired(now)) { if (cookieSpec.match(cookie, cookieOrigin)) { if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie {} match {}", exchangeId, cookie, cookieOrigin); } matchedCookies.add(cookie); } } else { if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie {} expired", exchangeId, cookie); } expired = true; } } // Per RFC 6265, 5.3 // The user agent must evict all expired cookies if, at any time, an expired cookie // exists in the cookie store if (expired) { cookieStore.clearExpired(now); } // Generate Cookie request headers if (!matchedCookies.isEmpty()) { final List
headers = cookieSpec.formatCookies(matchedCookies); for (final Header header : headers) { request.addHeader(header); } } // Stick the CookieSpec and CookieOrigin instances to the HTTP context // so they could be obtained by the response interceptor context.setAttribute(HttpClientContext.COOKIE_SPEC, cookieSpec); context.setAttribute(HttpClientContext.COOKIE_ORIGIN, cookieOrigin); } } RequestAuthCache.java000066400000000000000000000121501434266521000364760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.io.IOException; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.client5.http.auth.AuthCache; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.impl.RequestSupport; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Request interceptor that can preemptively authenticate against known hosts, * if there is a cached {@link AuthScheme} instance in the local * {@link AuthCache} associated with the given target or proxy host. * * @since 4.1 * * @deprecated Do not use. */ @Deprecated @Contract(threading = ThreadingBehavior.STATELESS) public class RequestAuthCache implements HttpRequestInterceptor { private static final Logger LOG = LoggerFactory.getLogger(RequestAuthCache.class); public RequestAuthCache() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); Args.notNull(context, "HTTP context"); final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); final AuthCache authCache = clientContext.getAuthCache(); if (authCache == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Auth cache not set in the context", exchangeId); } return; } final CredentialsProvider credsProvider = clientContext.getCredentialsProvider(); if (credsProvider == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Credentials provider not set in the context", exchangeId); } return; } final RouteInfo route = clientContext.getHttpRoute(); if (route == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Route info not set in the context", exchangeId); } return; } final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority()); final AuthExchange targetAuthExchange = clientContext.getAuthExchange(target); if (targetAuthExchange.getState() == AuthExchange.State.UNCHALLENGED) { final String pathPrefix = RequestSupport.extractPathPrefix(request); final AuthScheme authScheme = authCache.get(target, pathPrefix); if (authScheme != null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Re-using cached '{}' auth scheme for {}", exchangeId, authScheme.getName(), target); } targetAuthExchange.select(authScheme); } } final HttpHost proxy = route.getProxyHost(); if (proxy != null) { final AuthExchange proxyAuthExchange = clientContext.getAuthExchange(proxy); if (proxyAuthExchange.getState() == AuthExchange.State.UNCHALLENGED) { final AuthScheme authScheme = authCache.get(proxy, null); if (authScheme != null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Re-using cached '{}' auth scheme for {}", exchangeId, authScheme.getName(), proxy); } proxyAuthExchange.select(authScheme); } } } } } RequestClientConnControl.java000066400000000000000000000065651434266521000402630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.io.IOException; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This protocol interceptor is responsible for adding the {@code Connection} * header to the outgoing requests, which is essential for managing persistence * of {@code HTTP/1.0} connections. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class RequestClientConnControl implements HttpRequestInterceptor { private static final Logger LOG = LoggerFactory.getLogger(RequestClientConnControl.class); public RequestClientConnControl() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final String method = request.getMethod(); if (Method.CONNECT.isSame(method)) { return; } final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); // Obtain the client connection (required) final RouteInfo route = clientContext.getHttpRoute(); if (route == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Connection route not set in the context", exchangeId); } return; } if (route.getHopCount() == 1 || route.isTunnelled()) { if (!request.containsHeader(HttpHeaders.CONNECTION)) { request.addHeader(HttpHeaders.CONNECTION, HeaderElements.KEEP_ALIVE); } } } } RequestDefaultHeaders.java000066400000000000000000000057641434266521000375460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.io.IOException; import java.util.Collection; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * Request interceptor that adds default request headers. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class RequestDefaultHeaders implements HttpRequestInterceptor { /** * Singleton instance. * * @since 5.2 */ public static final RequestDefaultHeaders INSTANCE = new RequestDefaultHeaders(); private final Collection defaultHeaders; /** * @since 4.3 */ public RequestDefaultHeaders(final Collection defaultHeaders) { super(); this.defaultHeaders = defaultHeaders; } public RequestDefaultHeaders() { this(null); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); final String method = request.getMethod(); if (Method.CONNECT.isSame(method)) { return; } if (this.defaultHeaders != null) { for (final Header defHeader : this.defaultHeaders) { if(!request.containsHeader(defHeader.getName())) { request.addHeader(defHeader); } } } } } RequestExpectContinue.java000066400000000000000000000063561434266521000376210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.io.IOException; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolVersion; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; /** * RequestExpectContinue is responsible for enabling the 'expect-continue' * handshake by adding {@code Expect} header. *

* This interceptor takes into account {@link RequestConfig#isExpectContinueEnabled()} * setting. *

* * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class RequestExpectContinue implements HttpRequestInterceptor { public RequestExpectContinue() { super(); } @Override public void process(final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(request, "HTTP request"); if (!request.containsHeader(HttpHeaders.EXPECT)) { final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : HttpVersion.HTTP_1_1; // Do not send the expect header if request body is known to be empty if (entity != null && entity.getContentLength() != 0 && !version.lessEquals(HttpVersion.HTTP_1_0)) { final HttpClientContext clientContext = HttpClientContext.adapt(context); final RequestConfig config = clientContext.getRequestConfig(); if (config.isExpectContinueEnabled()) { request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE); } } } } } ResponseProcessCookies.java000066400000000000000000000142761434266521000377650ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.io.IOException; import java.util.Iterator; import java.util.List; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Response interceptor that populates the current {@link CookieStore} with data * contained in response cookies received in the given the HTTP response. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class ResponseProcessCookies implements HttpResponseInterceptor { /** * Singleton instance. * * @since 5.2 */ public static final ResponseProcessCookies INSTANCE = new ResponseProcessCookies(); private static final Logger LOG = LoggerFactory.getLogger(ResponseProcessCookies.class); public ResponseProcessCookies() { super(); } @Override public void process(final HttpResponse response, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { Args.notNull(response, "HTTP request"); Args.notNull(context, "HTTP context"); final HttpClientContext clientContext = HttpClientContext.adapt(context); final String exchangeId = clientContext.getExchangeId(); // Obtain actual CookieSpec instance final CookieSpec cookieSpec = clientContext.getCookieSpec(); if (cookieSpec == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie spec not specified in HTTP context", exchangeId); } return; } // Obtain cookie store final CookieStore cookieStore = clientContext.getCookieStore(); if (cookieStore == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie store not specified in HTTP context", exchangeId); } return; } // Obtain actual CookieOrigin instance final CookieOrigin cookieOrigin = clientContext.getCookieOrigin(); if (cookieOrigin == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie origin not specified in HTTP context", exchangeId); } return; } final Iterator
it = response.headerIterator(HttpHeaders.SET_COOKIE); processCookies(exchangeId, it, cookieSpec, cookieOrigin, cookieStore); } private void processCookies( final String exchangeId, final Iterator
iterator, final CookieSpec cookieSpec, final CookieOrigin cookieOrigin, final CookieStore cookieStore) { while (iterator.hasNext()) { final Header header = iterator.next(); try { final List cookies = cookieSpec.parse(header, cookieOrigin); for (final Cookie cookie : cookies) { try { cookieSpec.validate(cookie, cookieOrigin); cookieStore.addCookie(cookie); if (LOG.isDebugEnabled()) { LOG.debug("{} Cookie accepted [{}]", exchangeId, formatCookie(cookie)); } } catch (final MalformedCookieException ex) { if (LOG.isWarnEnabled()) { LOG.warn("{} Cookie rejected [{}] {}", exchangeId, formatCookie(cookie), ex.getMessage()); } } } } catch (final MalformedCookieException ex) { if (LOG.isWarnEnabled()) { LOG.warn("{} Invalid cookie header: \"{}\". {}", exchangeId, header, ex.getMessage()); } } } } private static String formatCookie(final Cookie cookie) { final StringBuilder buf = new StringBuilder(); buf.append(cookie.getName()); buf.append("=\""); String v = cookie.getValue(); if (v != null) { if (v.length() > 100) { v = v.substring(0, 100) + "..."; } buf.append(v); } buf.append("\""); buf.append(", domain:"); buf.append(cookie.getDomain()); buf.append(", path:"); buf.append(cookie.getPath()); buf.append(", expiry:"); buf.append(cookie.getExpiryInstant()); return buf.toString(); } } package-info.java000066400000000000000000000025321434266521000356270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * HTTP protocol interceptors that enable advanced functionality * such as HTTP state management and authentication state caching. */ package org.apache.hc.client5.http.protocol; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/psl/000077500000000000000000000000001434266521000314525ustar00rootroot00000000000000DomainType.java000066400000000000000000000025311434266521000343100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.psl; /** * Domain types differentiated by Mozilla Public Suffix List. * * @since 4.5 */ public enum DomainType { UNKNOWN, ICANN, PRIVATE } PublicSuffixList.java000066400000000000000000000053161434266521000355020ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.psl; import java.util.Collections; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Public suffix is a set of DNS names or wildcards concatenated with dots. It represents * the part of a domain name which is not under the control of the individual registrant *

* An up-to-date list of suffixes can be obtained from * publicsuffix.org *

* * @since 4.4 */ @Contract(threading = ThreadingBehavior.IMMUTABLE) public final class PublicSuffixList { private final DomainType type; private final List rules; private final List exceptions; /** * @since 4.5 */ public PublicSuffixList(final DomainType type, final List rules, final List exceptions) { this.type = Args.notNull(type, "Domain type"); this.rules = Collections.unmodifiableList(Args.notNull(rules, "Domain suffix rules")); this.exceptions = Collections.unmodifiableList(exceptions != null ? exceptions : Collections.emptyList()); } public PublicSuffixList(final List rules, final List exceptions) { this(DomainType.UNKNOWN, rules, exceptions); } /** * @since 4.5 */ public DomainType getType() { return type; } public List getRules() { return rules; } public List getExceptions() { return exceptions; } } PublicSuffixListParser.java000066400000000000000000000137151434266521000366610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.psl; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Parses the list from publicsuffix.org * and configures a PublicSuffixFilter. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public final class PublicSuffixListParser { /** * Singleton instance. * * @since 5.2 */ public static final PublicSuffixListParser INSTANCE = new PublicSuffixListParser(); public PublicSuffixListParser() { } /** * Parses the public suffix list format. *

* When creating the reader from the file, make sure to use the correct encoding * (the original list is in UTF-8). * * @param reader the data reader. The caller is responsible for closing the reader. * @throws java.io.IOException on error while reading from list */ public PublicSuffixList parse(final Reader reader) throws IOException { final List rules = new ArrayList<>(); final List exceptions = new ArrayList<>(); final BufferedReader r = new BufferedReader(reader); String line; while ((line = r.readLine()) != null) { if (line.isEmpty()) { continue; } if (line.startsWith("//")) { continue; //entire lines can also be commented using // } if (line.startsWith(".")) { line = line.substring(1); // A leading dot is optional } // An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule final boolean isException = line.startsWith("!"); if (isException) { line = line.substring(1); } if (isException) { exceptions.add(line); } else { rules.add(line); } } return new PublicSuffixList(DomainType.UNKNOWN, rules, exceptions); } /** * Parses the public suffix list format by domain type (currently supported ICANN and PRIVATE). *

* When creating the reader from the file, make sure to use the correct encoding * (the original list is in UTF-8). * * @param reader the data reader. The caller is responsible for closing the reader. * @throws java.io.IOException on error while reading from list * * @since 4.5 */ public List parseByType(final Reader reader) throws IOException { final List result = new ArrayList<>(2); final BufferedReader r = new BufferedReader(reader); DomainType domainType = null; List rules = null; List exceptions = null; String line; while ((line = r.readLine()) != null) { if (line.isEmpty()) { continue; } if (line.startsWith("//")) { if (domainType == null) { if (line.contains("===BEGIN ICANN DOMAINS===")) { domainType = DomainType.ICANN; } else if (line.contains("===BEGIN PRIVATE DOMAINS===")) { domainType = DomainType.PRIVATE; } } else { if (line.contains("===END ICANN DOMAINS===") || line.contains("===END PRIVATE DOMAINS===")) { if (rules != null) { result.add(new PublicSuffixList(domainType, rules, exceptions)); } domainType = null; rules = null; exceptions = null; } } continue; //entire lines can also be commented using // } if (domainType == null) { continue; } if (line.startsWith(".")) { line = line.substring(1); // A leading dot is optional } // An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule final boolean isException = line.startsWith("!"); if (isException) { line = line.substring(1); } if (isException) { if (exceptions == null) { exceptions = new ArrayList<>(); } exceptions.add(line); } else { if (rules == null) { rules = new ArrayList<>(); } rules.add(line); } } return result; } } PublicSuffixMatcher.java000066400000000000000000000162541434266521000361550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.psl; import java.net.IDN; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.utils.DnsUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; /** * Utility class that can test if DNS names match the content of the Public Suffix List. *

* An up-to-date list of suffixes can be obtained from * publicsuffix.org *

* * @see PublicSuffixList * * @since 4.4 */ @Contract(threading = ThreadingBehavior.SAFE) public final class PublicSuffixMatcher { private final Map rules; private final Map exceptions; public PublicSuffixMatcher(final Collection rules, final Collection exceptions) { this(DomainType.UNKNOWN, rules, exceptions); } /** * @since 4.5 */ public PublicSuffixMatcher( final DomainType domainType, final Collection rules, final Collection exceptions) { Args.notNull(domainType, "Domain type"); Args.notNull(rules, "Domain suffix rules"); this.rules = new ConcurrentHashMap<>(rules.size()); for (final String rule: rules) { this.rules.put(rule, domainType); } this.exceptions = new ConcurrentHashMap<>(); if (exceptions != null) { for (final String exception: exceptions) { this.exceptions.put(exception, domainType); } } } /** * @since 4.5 */ public PublicSuffixMatcher(final Collection lists) { Args.notNull(lists, "Domain suffix lists"); this.rules = new ConcurrentHashMap<>(); this.exceptions = new ConcurrentHashMap<>(); for (final PublicSuffixList list: lists) { final DomainType domainType = list.getType(); final List rules = list.getRules(); for (final String rule: rules) { this.rules.put(rule, domainType); } final List exceptions = list.getExceptions(); if (exceptions != null) { for (final String exception: exceptions) { this.exceptions.put(exception, domainType); } } } } private static DomainType findEntry(final Map map, final String rule) { if (map == null) { return null; } return map.get(rule); } private static boolean match(final DomainType domainType, final DomainType expectedType) { return domainType != null && (expectedType == null || domainType.equals(expectedType)); } /** * Returns registrable part of the domain for the given domain name or {@code null} * if given domain represents a public suffix. * * @param domain * @return domain root */ public String getDomainRoot(final String domain) { return getDomainRoot(domain, null); } /** * Returns registrable part of the domain for the given domain name or {@code null} * if given domain represents a public suffix. * * @param domain * @param expectedType expected domain type or {@code null} if any. * @return domain root * * @since 4.5 */ public String getDomainRoot(final String domain, final DomainType expectedType) { if (domain == null) { return null; } if (domain.startsWith(".")) { return null; } String segment = DnsUtils.normalize(domain); String result = null; while (segment != null) { // An exception rule takes priority over any other matching rule. final String key = IDN.toUnicode(segment); final DomainType exceptionRule = findEntry(exceptions, key); if (match(exceptionRule, expectedType)) { return segment; } final DomainType domainRule = findEntry(rules, key); if (match(domainRule, expectedType)) { if (domainRule == DomainType.PRIVATE) { return segment; } return result; } final int nextdot = segment.indexOf('.'); final String nextSegment = nextdot != -1 ? segment.substring(nextdot + 1) : null; if (nextSegment != null) { final DomainType wildcardDomainRule = findEntry(rules, "*." + IDN.toUnicode(nextSegment)); if (match(wildcardDomainRule, expectedType)) { if (wildcardDomainRule == DomainType.PRIVATE) { return segment; } return result; } } result = segment; segment = nextSegment; } // If no expectations then this result is good. if (expectedType == null || expectedType == DomainType.UNKNOWN) { return result; } // If we did have expectations apparently there was no match return null; } /** * Tests whether the given domain matches any of entry from the public suffix list. */ public boolean matches(final String domain) { return matches(domain, null); } /** * Tests whether the given domain matches any of entry from the public suffix list. * * @param domain * @param expectedType expected domain type or {@code null} if any. * @return {@code true} if the given domain matches any of the public suffixes. * * @since 4.5 */ public boolean matches(final String domain, final DomainType expectedType) { if (domain == null) { return false; } final String domainRoot = getDomainRoot( domain.startsWith(".") ? domain.substring(1) : domain, expectedType); return domainRoot == null; } } PublicSuffixMatcherLoader.java000066400000000000000000000071741434266521000373050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.psl; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link PublicSuffixMatcher} loader. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.SAFE) public final class PublicSuffixMatcherLoader { private static final Logger LOG = LoggerFactory.getLogger(PublicSuffixMatcherLoader.class); private static PublicSuffixMatcher load(final InputStream in) throws IOException { final List lists = PublicSuffixListParser.INSTANCE.parseByType( new InputStreamReader(in, StandardCharsets.UTF_8)); return new PublicSuffixMatcher(lists); } public static PublicSuffixMatcher load(final URL url) throws IOException { Args.notNull(url, "URL"); try (InputStream in = url.openStream()) { return load(in); } } public static PublicSuffixMatcher load(final File file) throws IOException { Args.notNull(file, "File"); try (InputStream in = new FileInputStream(file)) { return load(in); } } private static volatile PublicSuffixMatcher DEFAULT_INSTANCE; public static PublicSuffixMatcher getDefault() { if (DEFAULT_INSTANCE == null) { synchronized (PublicSuffixMatcherLoader.class) { if (DEFAULT_INSTANCE == null){ final URL url = PublicSuffixMatcherLoader.class.getResource( "/mozilla/public-suffix-list.txt"); if (url != null) { try { DEFAULT_INSTANCE = load(url); } catch (final IOException ex) { // Should never happen LOG.warn("Failure loading public suffix list from default resource", ex); } } else { DEFAULT_INSTANCE = new PublicSuffixMatcher(DomainType.ICANN, Collections.singletonList("com"), null); } } } } return DEFAULT_INSTANCE; } } package-info.java000066400000000000000000000024401434266521000345620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Public Suffix List support classes. */ package org.apache.hc.client5.http.psl; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/routing/000077500000000000000000000000001434266521000323435ustar00rootroot00000000000000HttpRouteDirector.java000066400000000000000000000052151434266521000365640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.routing; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Provides directions on establishing a route. * Implementations of this interface compare a planned route with * a tracked route and indicate the next step required. * * @since 4.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpRouteDirector { /** Indicates that the route can not be established at all. */ int UNREACHABLE = -1; /** Indicates that the route is complete. */ int COMPLETE = 0; /** Step: open connection to target. */ int CONNECT_TARGET = 1; /** Step: open connection to proxy. */ int CONNECT_PROXY = 2; /** Step: tunnel through proxy to target. */ int TUNNEL_TARGET = 3; /** Step: tunnel through proxy to other proxy. */ int TUNNEL_PROXY = 4; /** Step: layer protocol (over tunnel). */ int LAYER_PROTOCOL = 5; /** * Provides the next step. * * @param plan the planned route * @param fact the currently established route, or * {@code null} if nothing is established * * @return one of the constants defined in this interface, indicating * either the next step to perform, or success, or failure. * 0 is for success, a negative value for failure. */ int nextStep(RouteInfo plan, RouteInfo fact); } HttpRoutePlanner.java000066400000000000000000000046631434266521000364160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.routing; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; /** * Encapsulates logic to compute a {@link HttpRoute} to a target host. * Implementations may for example be based on parameters, or on the * standard Java system properties. *

* 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 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpRoutePlanner { /** * Determines the route for the given host. * * @param target the target host for the request. * @param context the context to use for the subsequent execution. * Implementations may accept {@code null}. * * @return the route that the request should take * * @throws HttpException in case of a problem */ HttpRoute determineRoute(HttpHost target, HttpContext context) throws HttpException; } RoutingSupport.java000066400000000000000000000062351434266521000361610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.routing; import java.net.URI; import java.net.URISyntaxException; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.utils.URIUtils; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.net.URIAuthority; public final class RoutingSupport { public static HttpHost determineHost(final HttpRequest request) throws HttpException { if (request == null) { return null; } final URIAuthority authority = request.getAuthority(); if (authority != null) { final String scheme = request.getScheme(); if (scheme == null) { throw new ProtocolException("Protocol scheme is not specified"); } return new HttpHost(scheme, authority); } try { final URI requestURI = request.getUri(); if (requestURI.isAbsolute()) { final HttpHost httpHost = URIUtils.extractHost(requestURI); if (httpHost == null) { throw new ProtocolException("URI does not specify a valid host name: " + requestURI); } return httpHost; } } catch (final URISyntaxException ignore) { } return null; } public static HttpHost normalize(final HttpHost host, final SchemePortResolver schemePortResolver) { if (host == null) { return null; } if (host.getPort() < 0) { final int port = (schemePortResolver != null ? schemePortResolver: DefaultSchemePortResolver.INSTANCE).resolve(host); if (port > 0) { return new HttpHost(host.getSchemeName(), host.getAddress(), host.getHostName(), port); } } return host; } } package-info.java000066400000000000000000000023701434266521000354550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client connection routing APIs. */ package org.apache.hc.client5.http.routing; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/socket/000077500000000000000000000000001434266521000321445ustar00rootroot00000000000000ConnectionSocketFactory.java000066400000000000000000000110551434266521000375320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/socket/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.socket; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * A factory for creating and connecting connection sockets. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface ConnectionSocketFactory { /** * Creates new, unconnected socket. The socket should subsequently be passed to * {@link #connectSocket(TimeValue, Socket, HttpHost, InetSocketAddress, InetSocketAddress, * HttpContext) connectSocket} method. * * @return a new socket * * @throws IOException if an I/O error occurs while creating the socket */ Socket createSocket(HttpContext context) throws IOException; /** * Connects the socket to the target host with the given resolved remote address. * * @param connectTimeout connect timeout. * @param socket the socket to connect, as obtained from {@link #createSocket(HttpContext)}. * {@code null} indicates that a new socket should be created and connected. * @param host target host as specified by the caller (end user). * @param remoteAddress the resolved remote address to connect to. * @param localAddress the local address to bind the socket to, or {@code null} for any. * @param context the actual HTTP context. * * @return the connected socket. The returned object may be different * from the {@code sock} argument if this factory supports * a layered protocol. * * @throws IOException if an I/O error occurs */ Socket connectSocket( TimeValue connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException; /** * Connects the socket to the target host with the given resolved remote address. * * @param socket the socket to connect, as obtained from {@link #createSocket(HttpContext)}. * {@code null} indicates that a new socket should be created and connected. * @param host target host as specified by the caller (end user). * @param remoteAddress the resolved remote address to connect to. * @param localAddress the local address to bind the socket to, or {@code null} for any. * @param connectTimeout connect timeout. * @param attachment connect request attachment. * @param context the actual HTTP context. * * @return the connected socket. The returned object may be different * from the {@code sock} argument if this factory supports * a layered protocol. * * @throws IOException if an I/O error occurs * * @since 5.2 */ default Socket connectSocket( Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, Timeout connectTimeout, Object attachment, HttpContext context) throws IOException { return connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context); } } LayeredConnectionSocketFactory.java000066400000000000000000000062311434266521000410400ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/socket/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.socket; import java.io.IOException; import java.net.Socket; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.protocol.HttpContext; /** * Extended {@link ConnectionSocketFactory} interface for layered sockets such as SSL/TLS. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface LayeredConnectionSocketFactory extends ConnectionSocketFactory { /** * Returns a socket connected to the given host that is layered over an * existing socket. Used primarily for creating secure sockets through * proxies. * * @param socket the existing socket * @param target the name of the target host. * @param port the port to connect to on the target host. * @param context the actual HTTP context. * * @return Socket a new socket * * @throws IOException if an I/O error occurs while creating the socket */ Socket createLayeredSocket( Socket socket, String target, int port, HttpContext context) throws IOException; /** * Returns a socket connected to the given host that is layered over an * existing socket. Used primarily for creating secure sockets through * proxies. * * @param socket the existing socket * @param target the name of the target host. * @param port the port to connect to on the target host. * @param context the actual HTTP context. * @param attachment connect request attachment. * * @return Socket a new socket * * @throws IOException if an I/O error occurs while creating the socket * * @since 5.2 */ default Socket createLayeredSocket( Socket socket, String target, int port, Object attachment, HttpContext context) throws IOException { return createLayeredSocket(socket, target, port, context); } } PlainConnectionSocketFactory.java000066400000000000000000000074771434266521000405330ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/socket/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.socket; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.TimeValue; /** * The default class for creating plain (unencrypted) sockets. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class PlainConnectionSocketFactory implements ConnectionSocketFactory { public static final PlainConnectionSocketFactory INSTANCE = new PlainConnectionSocketFactory(); public static PlainConnectionSocketFactory getSocketFactory() { return INSTANCE; } public PlainConnectionSocketFactory() { super(); } @Override public Socket createSocket(final HttpContext context) throws IOException { return new Socket(); } @Override public Socket connectSocket( final TimeValue connectTimeout, final Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) throws IOException { final Socket sock = socket != null ? socket : createSocket(context); if (localAddress != null) { sock.bind(localAddress); } try { // Run this under a doPrivileged to support lib users that run under a SecurityManager this allows granting connect permissions // only to this library try { AccessController.doPrivileged((PrivilegedExceptionAction) () -> { sock.connect(remoteAddress, TimeValue.isPositive(connectTimeout) ? connectTimeout.toMillisecondsIntBound() : 0); return null; }); } catch (final PrivilegedActionException e) { Asserts.check(e.getCause() instanceof IOException, "method contract violation only checked exceptions are wrapped: " + e.getCause()); // only checked exceptions are wrapped - error and RTExceptions are rethrown by doPrivileged throw (IOException) e.getCause(); } } catch (final IOException ex) { Closer.closeQuietly(sock); throw ex; } return sock; } } package-info.java000066400000000000000000000023661434266521000352630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/socket/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client connection socket APIs. */ package org.apache.hc.client5.http.socket; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/000077500000000000000000000000001434266521000314555ustar00rootroot00000000000000AbstractClientTlsStrategy.java000066400000000000000000000172401434266521000373550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.net.SocketAddress; import java.util.Arrays; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.http.ssl.TlsCiphers; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.ssl.ApplicationProtocol; import org.apache.hc.core5.http2.ssl.H2TlsSupport; import org.apache.hc.core5.net.NamedEndpoint; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Contract(threading = ThreadingBehavior.STATELESS) abstract class AbstractClientTlsStrategy implements TlsStrategy { private static final Logger LOG = LoggerFactory.getLogger(AbstractClientTlsStrategy.class); private final SSLContext sslContext; private final String[] supportedProtocols; private final String[] supportedCipherSuites; private final SSLBufferMode sslBufferManagement; private final HostnameVerifier hostnameVerifier; private final TlsSessionValidator tlsSessionValidator; AbstractClientTlsStrategy( final SSLContext sslContext, final String[] supportedProtocols, final String[] supportedCipherSuites, final SSLBufferMode sslBufferManagement, final HostnameVerifier hostnameVerifier) { super(); this.sslContext = Args.notNull(sslContext, "SSL context"); this.supportedProtocols = supportedProtocols; this.supportedCipherSuites = supportedCipherSuites; this.sslBufferManagement = sslBufferManagement != null ? sslBufferManagement : SSLBufferMode.STATIC; this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier(); this.tlsSessionValidator = new TlsSessionValidator(LOG); } /** * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)} */ @Deprecated @Override public boolean upgrade( final TransportSecurityLayer tlsSession, final HttpHost host, final SocketAddress localAddress, final SocketAddress remoteAddress, final Object attachment, final Timeout handshakeTimeout) { upgrade(tlsSession, host, attachment, handshakeTimeout, null); return true; } @Override public void upgrade( final TransportSecurityLayer tlsSession, final NamedEndpoint endpoint, final Object attachment, final Timeout handshakeTimeout, final FutureCallback callback) { tlsSession.startTls(sslContext, endpoint, sslBufferManagement, (e, sslEngine) -> { final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT; final HttpVersionPolicy versionPolicy = tlsConfig.getHttpVersionPolicy(); final SSLParameters sslParameters = sslEngine.getSSLParameters(); final String[] supportedProtocols = tlsConfig.getSupportedProtocols(); if (supportedProtocols != null) { sslParameters.setProtocols(supportedProtocols); } else if (this.supportedProtocols != null) { sslParameters.setProtocols(this.supportedProtocols); } else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) { sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols())); } final String[] supportedCipherSuites = tlsConfig.getSupportedCipherSuites(); if (supportedCipherSuites != null) { sslParameters.setCipherSuites(supportedCipherSuites); } else if (this.supportedCipherSuites != null) { sslParameters.setCipherSuites(this.supportedCipherSuites); } else if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) { sslParameters.setCipherSuites(TlsCiphers.excludeH2Blacklisted(sslParameters.getCipherSuites())); } if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) { H2TlsSupport.setEnableRetransmissions(sslParameters, false); } applyParameters(sslEngine, sslParameters, H2TlsSupport.selectApplicationProtocols(versionPolicy)); initializeEngine(sslEngine); if (LOG.isDebugEnabled()) { LOG.debug("Enabled protocols: {}", Arrays.asList(sslEngine.getEnabledProtocols())); LOG.debug("Enabled cipher suites:{}", Arrays.asList(sslEngine.getEnabledCipherSuites())); LOG.debug("Starting handshake ({})", handshakeTimeout); } }, (e, sslEngine) -> { verifySession(endpoint.getHostName(), sslEngine.getSession()); final TlsDetails tlsDetails = createTlsDetails(sslEngine); final String negotiatedCipherSuite = sslEngine.getSession().getCipherSuite(); if (tlsDetails != null && ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) { if (TlsCiphers.isH2Blacklisted(negotiatedCipherSuite)) { throw new SSLHandshakeException("Cipher suite `" + negotiatedCipherSuite + "` does not provide adequate security for HTTP/2"); } } return tlsDetails; }, handshakeTimeout, callback); } abstract void applyParameters(SSLEngine sslEngine, SSLParameters sslParameters, String[] appProtocols); abstract TlsDetails createTlsDetails(SSLEngine sslEngine); protected void initializeEngine(final SSLEngine sslEngine) { } protected void verifySession( final String hostname, final SSLSession sslsession) throws SSLException { tlsSessionValidator.verifySession(hostname, sslsession, hostnameVerifier); } } ClientTlsStrategyBuilder.java000066400000000000000000000140161434266521000371760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import org.apache.hc.core5.function.Factory; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.ssl.SSLContexts; /** * Builder for client {@link TlsStrategy} instances. *

* When a particular component is not explicitly set 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()}. *

*
    *
  • ssl.TrustManagerFactory.algorithm
  • *
  • javax.net.ssl.trustStoreType
  • *
  • javax.net.ssl.trustStore
  • *
  • javax.net.ssl.trustStoreProvider
  • *
  • javax.net.ssl.trustStorePassword
  • *
  • ssl.KeyManagerFactory.algorithm
  • *
  • javax.net.ssl.keyStoreType
  • *
  • javax.net.ssl.keyStore
  • *
  • javax.net.ssl.keyStoreProvider
  • *
  • javax.net.ssl.keyStorePassword
  • *
  • https.protocols
  • *
  • https.cipherSuites
  • *
* * @since 5.0 */ public class ClientTlsStrategyBuilder { public static ClientTlsStrategyBuilder create() { return new ClientTlsStrategyBuilder(); } private SSLContext sslContext; private String[] tlsVersions; private String[] ciphers; private SSLBufferMode sslBufferMode; private HostnameVerifier hostnameVerifier; /** * @deprecated To be removed. */ @Deprecated private Factory tlsDetailsFactory; private boolean systemProperties; /** * Assigns {@link SSLContext} instance. */ public ClientTlsStrategyBuilder setSslContext(final SSLContext sslContext) { this.sslContext = sslContext; return this; } /** * Assigns enabled {@code TLS} versions. */ public final ClientTlsStrategyBuilder setTlsVersions(final String... tlslVersions) { this.tlsVersions = tlslVersions; return this; } /** * Assigns enabled {@code TLS} versions. */ public final ClientTlsStrategyBuilder setTlsVersions(final TLS... tlslVersions) { this.tlsVersions = new String[tlslVersions.length]; for (int i = 0; i < tlslVersions.length; i++) { this.tlsVersions[i] = tlslVersions[i].id; } return this; } /** * Assigns enabled ciphers. */ public final ClientTlsStrategyBuilder setCiphers(final String... ciphers) { this.ciphers = ciphers; return this; } /** * Assigns {@link SSLBufferMode} value. */ public ClientTlsStrategyBuilder setSslBufferMode(final SSLBufferMode sslBufferMode) { this.sslBufferMode = sslBufferMode; return this; } /** * Assigns {@link HostnameVerifier} instance. */ public ClientTlsStrategyBuilder setHostnameVerifier(final HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; return this; } /** * Assigns {@link TlsDetails} {@link Factory} instance. * * @deprecated Do not use. */ @Deprecated public ClientTlsStrategyBuilder setTlsDetailsFactory(final Factory tlsDetailsFactory) { this.tlsDetailsFactory = tlsDetailsFactory; return this; } /** * Use system properties when creating and configuring default * implementations. */ public final ClientTlsStrategyBuilder useSystemProperties() { this.systemProperties = true; return this; } @SuppressWarnings("deprecation") public TlsStrategy build() { final SSLContext sslContextCopy; if (sslContext != null) { sslContextCopy = sslContext; } else { sslContextCopy = systemProperties ? SSLContexts.createSystemDefault() : SSLContexts.createDefault(); } final String[] tlsVersionsCopy; if (tlsVersions != null) { tlsVersionsCopy = tlsVersions; } else { tlsVersionsCopy = systemProperties ? HttpsSupport.getSystemProtocols() : null; } final String[] ciphersCopy; if (ciphers != null) { ciphersCopy = ciphers; } else { ciphersCopy = systemProperties ? HttpsSupport.getSystemCipherSuits() : null; } return new DefaultClientTlsStrategy( sslContextCopy, tlsVersionsCopy, ciphersCopy, sslBufferMode != null ? sslBufferMode : SSLBufferMode.STATIC, hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier(), tlsDetailsFactory); } } ConscryptClientTlsStrategy.java000066400000000000000000000106601434266521000375750ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.ssl.SSLContexts; import org.conscrypt.Conscrypt; /** * TLS upgrade strategy for non-blocking client connections using Conscrypt TLS library. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class ConscryptClientTlsStrategy extends AbstractClientTlsStrategy { public static TlsStrategy getDefault() { return new ConscryptClientTlsStrategy( SSLContexts.createDefault(), HttpsSupport.getDefaultHostnameVerifier()); } public static TlsStrategy getSystemDefault() { return new ConscryptClientTlsStrategy( SSLContexts.createSystemDefault(), HttpsSupport.getSystemProtocols(), HttpsSupport.getSystemCipherSuits(), SSLBufferMode.STATIC, HttpsSupport.getDefaultHostnameVerifier()); } public ConscryptClientTlsStrategy( final SSLContext sslContext, final String[] supportedProtocols, final String[] supportedCipherSuites, final SSLBufferMode sslBufferManagement, final HostnameVerifier hostnameVerifier) { super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerifier); } public ConscryptClientTlsStrategy( final SSLContext sslcontext, final HostnameVerifier hostnameVerifier) { this(sslcontext, null, null, SSLBufferMode.STATIC, hostnameVerifier); } public ConscryptClientTlsStrategy(final SSLContext sslcontext) { this(sslcontext, HttpsSupport.getDefaultHostnameVerifier()); } @Override void applyParameters(final SSLEngine sslEngine, final SSLParameters sslParameters, final String[] appProtocols) { if (Conscrypt.isConscrypt(sslEngine)) { sslEngine.setSSLParameters(sslParameters); Conscrypt.setApplicationProtocols(sslEngine, appProtocols); } else { sslParameters.setApplicationProtocols(appProtocols); sslEngine.setSSLParameters(sslParameters); } } @Override TlsDetails createTlsDetails(final SSLEngine sslEngine) { if (Conscrypt.isConscrypt(sslEngine)) { return new TlsDetails(sslEngine.getSession(), Conscrypt.getApplicationProtocol(sslEngine)); } return null; } public static boolean isSupported() { try { final Class clazz = Class.forName("org.conscrypt.Conscrypt"); final Method method = clazz.getMethod("isAvailable"); return ((Boolean) method.invoke(null)).booleanValue(); } catch (final ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { return false; } } } DefaultClientTlsStrategy.java000066400000000000000000000106661434266521000372030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.function.Factory; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.reactor.ssl.SSLBufferMode; import org.apache.hc.core5.reactor.ssl.TlsDetails; import org.apache.hc.core5.ssl.SSLContexts; /** * TLS upgrade strategy for non-blocking client connections. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class DefaultClientTlsStrategy extends AbstractClientTlsStrategy { public static TlsStrategy getDefault() { return new DefaultClientTlsStrategy( SSLContexts.createDefault(), HttpsSupport.getDefaultHostnameVerifier()); } public static TlsStrategy getSystemDefault() { return new DefaultClientTlsStrategy( SSLContexts.createSystemDefault(), HttpsSupport.getSystemProtocols(), HttpsSupport.getSystemCipherSuits(), SSLBufferMode.STATIC, HttpsSupport.getDefaultHostnameVerifier()); } /** * @deprecated To be removed. */ @Deprecated private Factory tlsDetailsFactory; /** * @deprecated Use {@link DefaultClientTlsStrategy#DefaultClientTlsStrategy(SSLContext, String[], String[], SSLBufferMode, HostnameVerifier)} */ @Deprecated public DefaultClientTlsStrategy( final SSLContext sslContext, final String[] supportedProtocols, final String[] supportedCipherSuites, final SSLBufferMode sslBufferManagement, final HostnameVerifier hostnameVerifier, final Factory tlsDetailsFactory) { super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerifier); this.tlsDetailsFactory = tlsDetailsFactory; } public DefaultClientTlsStrategy( final SSLContext sslContext, final String[] supportedProtocols, final String[] supportedCipherSuites, final SSLBufferMode sslBufferManagement, final HostnameVerifier hostnameVerifier) { super(sslContext, supportedProtocols, supportedCipherSuites, sslBufferManagement, hostnameVerifier); } public DefaultClientTlsStrategy( final SSLContext sslcontext, final HostnameVerifier hostnameVerifier) { this(sslcontext, null, null, SSLBufferMode.STATIC, hostnameVerifier); } public DefaultClientTlsStrategy(final SSLContext sslcontext) { this(sslcontext, HttpsSupport.getDefaultHostnameVerifier()); } @Override void applyParameters(final SSLEngine sslEngine, final SSLParameters sslParameters, final String[] appProtocols) { sslParameters.setApplicationProtocols(appProtocols); sslEngine.setSSLParameters(sslParameters); } @Override @SuppressWarnings("deprecated") TlsDetails createTlsDetails(final SSLEngine sslEngine) { return tlsDetailsFactory != null ? tlsDetailsFactory.create(sslEngine) : null; } } DefaultHostnameVerifier.java000066400000000000000000000330131434266521000370200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.cert.Certificate; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; import org.apache.hc.client5.http.psl.DomainType; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.apache.hc.client5.http.utils.DnsUtils; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.net.InetAddressUtils; import org.apache.hc.core5.util.TextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default {@link javax.net.ssl.HostnameVerifier} implementation. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier { enum HostNameType { IPv4(7), IPv6(7), DNS(2); final int subjectType; HostNameType(final int subjectType) { this.subjectType = subjectType; } } private static final Logger LOG = LoggerFactory.getLogger(DefaultHostnameVerifier.class); private final PublicSuffixMatcher publicSuffixMatcher; public DefaultHostnameVerifier(final PublicSuffixMatcher publicSuffixMatcher) { this.publicSuffixMatcher = publicSuffixMatcher; } public DefaultHostnameVerifier() { this(null); } @Override public boolean verify(final String host, final SSLSession session) { try { final Certificate[] certs = session.getPeerCertificates(); final X509Certificate x509 = (X509Certificate) certs[0]; verify(host, x509); return true; } catch (final SSLException ex) { if (LOG.isDebugEnabled()) { LOG.debug(ex.getMessage(), ex); } return false; } } @Override public void verify(final String host, final X509Certificate cert) throws SSLException { final HostNameType hostType = determineHostFormat(host); switch (hostType) { case IPv4: matchIPAddress(host, getSubjectAltNames(cert, SubjectName.IP)); break; case IPv6: matchIPv6Address(host, getSubjectAltNames(cert, SubjectName.IP)); break; default: final List subjectAlts = getSubjectAltNames(cert, SubjectName.DNS); if (subjectAlts.isEmpty()) { // CN matching has been deprecated by rfc2818 and can be used // as fallback only when no subjectAlts of type SubjectName.DNS are available matchCN(host, cert, this.publicSuffixMatcher); } else { matchDNSName(host, subjectAlts, this.publicSuffixMatcher); } } } static void matchIPAddress(final String host, final List subjectAlts) throws SSLException { for (int i = 0; i < subjectAlts.size(); i++) { final SubjectName subjectAlt = subjectAlts.get(i); if (subjectAlt.getType() == SubjectName.IP) { if (host.equals(subjectAlt.getValue())) { return; } } } throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match any " + "of the subject alternative names: " + subjectAlts); } static void matchIPv6Address(final String host, final List subjectAlts) throws SSLException { final String normalisedHost = normaliseAddress(host); for (int i = 0; i < subjectAlts.size(); i++) { final SubjectName subjectAlt = subjectAlts.get(i); if (subjectAlt.getType() == SubjectName.IP) { final String normalizedSubjectAlt = normaliseAddress(subjectAlt.getValue()); if (normalisedHost.equals(normalizedSubjectAlt)) { return; } } } throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match any " + "of the subject alternative names: " + subjectAlts); } static void matchDNSName(final String host, final List subjectAlts, final PublicSuffixMatcher publicSuffixMatcher) throws SSLException { final String normalizedHost = DnsUtils.normalize(host); for (int i = 0; i < subjectAlts.size(); i++) { final SubjectName subjectAlt = subjectAlts.get(i); if (subjectAlt.getType() == SubjectName.DNS) { final String normalizedSubjectAlt = DnsUtils.normalize(subjectAlt.getValue()); if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt, publicSuffixMatcher)) { return; } } } throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match any " + "of the subject alternative names: " + subjectAlts); } static void matchCN(final String host, final X509Certificate cert, final PublicSuffixMatcher publicSuffixMatcher) throws SSLException { final X500Principal subjectPrincipal = cert.getSubjectX500Principal(); final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253)); if (cn == null) { throw new SSLPeerUnverifiedException("Certificate subject for <" + host + "> doesn't contain " + "a common name and does not have alternative names"); } final String normalizedHost = DnsUtils.normalize(host); final String normalizedCn = DnsUtils.normalize(cn); if (!matchIdentityStrict(normalizedHost, normalizedCn, publicSuffixMatcher)) { throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match " + "common name of the certificate subject: " + cn); } } static boolean matchDomainRoot(final String host, final String domainRoot) { if (domainRoot == null) { return false; } return host.endsWith(domainRoot) && (host.length() == domainRoot.length() || host.charAt(host.length() - domainRoot.length() - 1) == '.'); } private static boolean matchIdentity(final String host, final String identity, final PublicSuffixMatcher publicSuffixMatcher, final DomainType domainType, final boolean strict) { if (publicSuffixMatcher != null && host.contains(".")) { if (!matchDomainRoot(host, publicSuffixMatcher.getDomainRoot(identity, domainType))) { return false; } } // RFC 2818, 3.1. Server Identity // "...Names may contain the wildcard // character * which is considered to match any single domain name // component or component fragment..." // Based on this statement presuming only singular wildcard is legal final int asteriskIdx = identity.indexOf('*'); if (asteriskIdx != -1) { final String prefix = identity.substring(0, asteriskIdx); final String suffix = identity.substring(asteriskIdx + 1); if (!prefix.isEmpty() && !host.startsWith(prefix)) { return false; } if (!suffix.isEmpty() && !host.endsWith(suffix)) { return false; } // Additional sanity checks on content selected by wildcard can be done here if (strict) { final String remainder = host.substring( prefix.length(), host.length() - suffix.length()); return !remainder.contains("."); } return true; } return host.equalsIgnoreCase(identity); } static boolean matchIdentity(final String host, final String identity, final PublicSuffixMatcher publicSuffixMatcher) { return matchIdentity(host, identity, publicSuffixMatcher, null, false); } static boolean matchIdentity(final String host, final String identity) { return matchIdentity(host, identity, null, null, false); } static boolean matchIdentityStrict(final String host, final String identity, final PublicSuffixMatcher publicSuffixMatcher) { return matchIdentity(host, identity, publicSuffixMatcher, null, true); } static boolean matchIdentityStrict(final String host, final String identity) { return matchIdentity(host, identity, null, null, true); } static boolean matchIdentity(final String host, final String identity, final PublicSuffixMatcher publicSuffixMatcher, final DomainType domainType) { return matchIdentity(host, identity, publicSuffixMatcher, domainType, false); } static boolean matchIdentityStrict(final String host, final String identity, final PublicSuffixMatcher publicSuffixMatcher, final DomainType domainType) { return matchIdentity(host, identity, publicSuffixMatcher, domainType, true); } static String extractCN(final String subjectPrincipal) throws SSLException { if (subjectPrincipal == null) { return null; } final List attributes = DistinguishedNameParser.INSTANCE.parse(subjectPrincipal); for (final NameValuePair attribute: attributes) { if (TextUtils.isBlank(attribute.getName()) || attribute.getValue() == null) { throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name"); } if (attribute.getName().equalsIgnoreCase("cn")) { return attribute.getValue(); } } return null; } static HostNameType determineHostFormat(final String host) { if (InetAddressUtils.isIPv4Address(host)) { return HostNameType.IPv4; } String s = host; if (s.startsWith("[") && s.endsWith("]")) { s = host.substring(1, host.length() - 1); } if (InetAddressUtils.isIPv6Address(s)) { return HostNameType.IPv6; } return HostNameType.DNS; } static List getSubjectAltNames(final X509Certificate cert) { return getSubjectAltNames(cert, -1); } static List getSubjectAltNames(final X509Certificate cert, final int subjectName) { try { final Collection> entries = cert.getSubjectAlternativeNames(); if (entries == null) { return Collections.emptyList(); } final List result = new ArrayList<>(); for (final List entry : entries) { final Integer type = entry.size() >= 2 ? (Integer) entry.get(0) : null; if (type != null) { if (type == subjectName || -1 == subjectName) { final Object o = entry.get(1); if (o instanceof String) { result.add(new SubjectName((String) o, type)); } else if (o instanceof byte[]) { // TODO ASN.1 DER encoded form } } } } return result; } catch (final CertificateParsingException ignore) { return Collections.emptyList(); } } /* * Normalize IPv6 or DNS name. */ static String normaliseAddress(final String hostname) { if (hostname == null) { return hostname; } try { final InetAddress inetAddress = InetAddress.getByName(hostname); return inetAddress.getHostAddress(); } catch (final UnknownHostException unexpected) { // Should not happen, because we check for IPv6 address above return hostname; } } } DistinguishedNameParser.java000066400000000000000000000114541434266521000370270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.Tokenizer; final class DistinguishedNameParser { public final static DistinguishedNameParser INSTANCE = new DistinguishedNameParser(); private static final BitSet EQUAL_OR_COMMA_OR_PLUS = Tokenizer.INIT_BITSET('=', ',', '+'); private static final BitSet COMMA_OR_PLUS = Tokenizer.INIT_BITSET(',', '+'); private final Tokenizer tokenParser; DistinguishedNameParser() { this.tokenParser = new InternalTokenParser(); } private String parseToken(final CharArrayBuffer buf, final Tokenizer.Cursor cursor, final BitSet delimiters) { return tokenParser.parseToken(buf, cursor, delimiters); } private String parseValue(final CharArrayBuffer buf, final Tokenizer.Cursor cursor, final BitSet delimiters) { return tokenParser.parseValue(buf, cursor, delimiters); } private NameValuePair parseParameter(final CharArrayBuffer buf, final Tokenizer.Cursor cursor) { final String name = parseToken(buf, cursor, EQUAL_OR_COMMA_OR_PLUS); if (cursor.atEnd()) { return new BasicNameValuePair(name, null); } final int delim = buf.charAt(cursor.getPos()); cursor.updatePos(cursor.getPos() + 1); if (delim == ',') { return new BasicNameValuePair(name, null); } final String value = parseValue(buf, cursor, COMMA_OR_PLUS); if (!cursor.atEnd()) { cursor.updatePos(cursor.getPos() + 1); } return new BasicNameValuePair(name, value); } List parse(final CharArrayBuffer buf, final Tokenizer.Cursor cursor) { final List params = new ArrayList<>(); tokenParser.skipWhiteSpace(buf, cursor); while (!cursor.atEnd()) { final NameValuePair param = parseParameter(buf, cursor); params.add(param); } return params; } List parse(final String s) { if (s == null) { return null; } final CharArrayBuffer buffer = new CharArrayBuffer(s.length()); buffer.append(s); final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length()); return parse(buffer, cursor); } static class InternalTokenParser extends Tokenizer { @Override public void copyUnquotedContent( final CharSequence buf, final Tokenizer.Cursor cursor, final BitSet delimiters, final StringBuilder dst) { int pos = cursor.getPos(); final int indexFrom = cursor.getPos(); final int indexTo = cursor.getUpperBound(); boolean escaped = false; for (int i = indexFrom; i < indexTo; i++, pos++) { final char current = buf.charAt(i); if (escaped) { dst.append(current); escaped = false; } else { if ((delimiters != null && delimiters.get(current)) || Tokenizer.isWhitespace(current) || current == '\"') { break; } else if (current == '\\') { escaped = true; } else { dst.append(current); } } } cursor.updatePos(pos); } } } HttpClientHostnameVerifier.java000066400000000000000000000036311434266521000375150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * Extended {@link HostnameVerifier} interface. * * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public interface HttpClientHostnameVerifier extends HostnameVerifier { /** * Verifies the supplied server {@link X509Certificate} and ensures it matches * the original host name. * * @param host the original host name. * @param cert the server certificate; */ void verify(String host, X509Certificate cert) throws SSLException; } HttpsSupport.java000066400000000000000000000043401434266521000347410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.security.AccessController; import java.security.PrivilegedAction; import javax.net.ssl.HostnameVerifier; import org.apache.hc.client5.http.psl.PublicSuffixMatcherLoader; import org.apache.hc.core5.util.TextUtils; /** * HTTPS configuration support methods. * * @since 5.0 */ public final class HttpsSupport { private static String[] split(final String s) { if (TextUtils.isBlank(s)) { return null; } return s.split(" *, *"); } private static String getProperty(final String key) { return AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(key)); } public static String[] getSystemProtocols() { return split(getProperty("https.protocols")); } public static String[] getSystemCipherSuits() { return split(getProperty("https.cipherSuites")); } public static HostnameVerifier getDefaultHostnameVerifier() { return new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault()); } } NoopHostnameVerifier.java000066400000000000000000000036671434266521000363630ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; /** * The NO_OP HostnameVerifier essentially turns hostname verification * off. This implementation is a no-op, and never throws the SSLException. * * @since 4.4 */ @Contract(threading = ThreadingBehavior.STATELESS) public class NoopHostnameVerifier implements HostnameVerifier { public static final NoopHostnameVerifier INSTANCE = new NoopHostnameVerifier(); @Override public boolean verify(final String s, final SSLSession sslSession) { return true; } @Override public final String toString() { return "NO_OP"; } } SSLConnectionSocketFactory.java000066400000000000000000000340431434266521000374270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.http.ssl.TlsCiphers; import org.apache.hc.core5.io.Closer; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.ssl.SSLInitializationException; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Asserts; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Layered socket factory for TLS/SSL connections. *

* SSLSocketFactory can be used to validate the identity of the HTTPS server against a list of * trusted certificates and to authenticate to the HTTPS server using a private key. * * @since 4.3 */ @Contract(threading = ThreadingBehavior.STATELESS) public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactory { private static final String WEAK_KEY_EXCHANGES = "^(TLS|SSL)_(NULL|ECDH_anon|DH_anon|DH_anon_EXPORT|DHE_RSA_EXPORT|DHE_DSS_EXPORT|" + "DSS_EXPORT|DH_DSS_EXPORT|DH_RSA_EXPORT|RSA_EXPORT|KRB5_EXPORT)_(.*)"; private static final String WEAK_CIPHERS = "^(TLS|SSL)_(.*)_WITH_(NULL|DES_CBC|DES40_CBC|DES_CBC_40|3DES_EDE_CBC|RC4_128|RC4_40|RC2_CBC_40)_(.*)"; private static final List WEAK_CIPHER_SUITE_PATTERNS = Collections.unmodifiableList(Arrays.asList( Pattern.compile(WEAK_KEY_EXCHANGES, Pattern.CASE_INSENSITIVE), Pattern.compile(WEAK_CIPHERS, Pattern.CASE_INSENSITIVE))); private static final Logger LOG = LoggerFactory.getLogger(SSLConnectionSocketFactory.class); /** * Obtains default SSL socket factory with an SSL context based on the standard JSSE * trust material ({@code cacerts} file in the security properties directory). * System properties are not taken into consideration. * * @return default SSL socket factory */ public static SSLConnectionSocketFactory getSocketFactory() throws SSLInitializationException { return new SSLConnectionSocketFactory(SSLContexts.createDefault(), HttpsSupport.getDefaultHostnameVerifier()); } /** * Obtains default SSL socket factory with an SSL context based on system properties * as described in * * Java™ Secure Socket Extension (JSSE) Reference Guide. * * @return default system SSL socket factory */ public static SSLConnectionSocketFactory getSystemSocketFactory() throws SSLInitializationException { return new SSLConnectionSocketFactory( (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(), HttpsSupport.getSystemProtocols(), HttpsSupport.getSystemCipherSuits(), HttpsSupport.getDefaultHostnameVerifier()); } static boolean isWeakCipherSuite(final String cipherSuite) { for (final Pattern pattern : WEAK_CIPHER_SUITE_PATTERNS) { if (pattern.matcher(cipherSuite).matches()) { return true; } } return false; } private final javax.net.ssl.SSLSocketFactory socketFactory; private final HostnameVerifier hostnameVerifier; private final String[] supportedProtocols; private final String[] supportedCipherSuites; private final TlsSessionValidator tlsSessionValidator; public SSLConnectionSocketFactory(final SSLContext sslContext) { this(sslContext, HttpsSupport.getDefaultHostnameVerifier()); } /** * @since 4.4 */ public SSLConnectionSocketFactory( final SSLContext sslContext, final HostnameVerifier hostnameVerifier) { this(Args.notNull(sslContext, "SSL context").getSocketFactory(), null, null, hostnameVerifier); } /** * @since 4.4 */ public SSLConnectionSocketFactory( final SSLContext sslContext, final String[] supportedProtocols, final String[] supportedCipherSuites, final HostnameVerifier hostnameVerifier) { this(Args.notNull(sslContext, "SSL context").getSocketFactory(), supportedProtocols, supportedCipherSuites, hostnameVerifier); } /** * @since 4.4 */ public SSLConnectionSocketFactory( final javax.net.ssl.SSLSocketFactory socketFactory, final HostnameVerifier hostnameVerifier) { this(socketFactory, null, null, hostnameVerifier); } /** * @since 4.4 */ public SSLConnectionSocketFactory( final javax.net.ssl.SSLSocketFactory socketFactory, final String[] supportedProtocols, final String[] supportedCipherSuites, final HostnameVerifier hostnameVerifier) { this.socketFactory = Args.notNull(socketFactory, "SSL socket factory"); this.supportedProtocols = supportedProtocols; this.supportedCipherSuites = supportedCipherSuites; this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier(); this.tlsSessionValidator = new TlsSessionValidator(LOG); } /** * Performs any custom initialization for a newly created SSLSocket * (before the SSL handshake happens). * * The default implementation is a no-op, but could be overridden to, e.g., * call {@link javax.net.ssl.SSLSocket#setEnabledCipherSuites(String[])}. * @throws IOException may be thrown if overridden */ protected void prepareSocket(final SSLSocket socket) throws IOException { } @Override public Socket createSocket(final HttpContext context) throws IOException { return SocketFactory.getDefault().createSocket(); } @Override public Socket connectSocket( final TimeValue connectTimeout, final Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) throws IOException { final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null; return connectSocket(socket, host, remoteAddress, localAddress, timeout, timeout, context); } @Override public Socket connectSocket( final Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final Timeout connectTimeout, final Object attachment, final HttpContext context) throws IOException { Args.notNull(host, "HTTP host"); Args.notNull(remoteAddress, "Remote address"); final Socket sock = socket != null ? socket : createSocket(context); if (localAddress != null) { sock.bind(localAddress); } try { if (LOG.isDebugEnabled()) { LOG.debug("Connecting socket to {} with timeout {}", remoteAddress, connectTimeout); } // Run this under a doPrivileged to support lib users that run under a SecurityManager this allows granting connect permissions // only to this library try { AccessController.doPrivileged((PrivilegedExceptionAction) () -> { sock.connect(remoteAddress, Timeout.defaultsToDisabled(connectTimeout).toMillisecondsIntBound()); return null; }); } catch (final PrivilegedActionException e) { Asserts.check(e.getCause() instanceof IOException, "method contract violation only checked exceptions are wrapped: " + e.getCause()); // only checked exceptions are wrapped - error and RTExceptions are rethrown by doPrivileged throw (IOException) e.getCause(); } } catch (final IOException ex) { Closer.closeQuietly(sock); throw ex; } // Setup SSL layering if necessary if (sock instanceof SSLSocket) { final SSLSocket sslsock = (SSLSocket) sock; executeHandshake(sslsock, host.getHostName(), attachment); return sock; } return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), attachment, context); } @Override public Socket createLayeredSocket( final Socket socket, final String target, final int port, final HttpContext context) throws IOException { return createLayeredSocket(socket, target, port, null, context); } @Override public Socket createLayeredSocket( final Socket socket, final String target, final int port, final Object attachment, final HttpContext context) throws IOException { final SSLSocket sslsock = (SSLSocket) this.socketFactory.createSocket( socket, target, port, true); executeHandshake(sslsock, target, attachment); return sslsock; } private void executeHandshake(final SSLSocket sslsock, final String target, final Object attachment) throws IOException { final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT; if (supportedProtocols != null) { sslsock.setEnabledProtocols(supportedProtocols); } else { sslsock.setEnabledProtocols((TLS.excludeWeak(sslsock.getEnabledProtocols()))); } if (supportedCipherSuites != null) { sslsock.setEnabledCipherSuites(supportedCipherSuites); } else { sslsock.setEnabledCipherSuites(TlsCiphers.excludeWeak(sslsock.getEnabledCipherSuites())); } final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout(); if (handshakeTimeout != null) { sslsock.setSoTimeout(handshakeTimeout.toMillisecondsIntBound()); } prepareSocket(sslsock); if (LOG.isDebugEnabled()) { LOG.debug("Enabled protocols: {}", (Object) sslsock.getEnabledProtocols()); LOG.debug("Enabled cipher suites: {}", (Object) sslsock.getEnabledCipherSuites()); LOG.debug("Starting handshake ({})", handshakeTimeout); } sslsock.startHandshake(); verifyHostname(sslsock, target); } private void verifyHostname(final SSLSocket sslsock, final String hostname) throws IOException { try { SSLSession session = sslsock.getSession(); if (session == null) { // In our experience this only happens under IBM 1.4.x when // spurious (unrelated) certificates show up in the server' // chain. Hopefully this will unearth the real problem: final InputStream in = sslsock.getInputStream(); in.available(); // If ssl.getInputStream().available() didn't cause an // exception, maybe at least now the session is available? session = sslsock.getSession(); if (session == null) { // If it's still null, probably a startHandshake() will // unearth the real problem. sslsock.startHandshake(); session = sslsock.getSession(); } } if (session == null) { throw new SSLHandshakeException("SSL session not available"); } verifySession(hostname, session); } catch (final IOException iox) { // close the socket before re-throwing the exception Closer.closeQuietly(sslsock); throw iox; } } protected void verifySession( final String hostname, final SSLSession sslSession) throws SSLException { tlsSessionValidator.verifySession(hostname, sslSession, hostnameVerifier); } } SSLConnectionSocketFactoryBuilder.java000066400000000000000000000123441434266521000407360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.ssl.SSLContexts; /** * Builder for {@link SSLConnectionSocketFactory} instances. *

* When a particular component is not explicitly set 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()}. *

*
    *
  • ssl.TrustManagerFactory.algorithm
  • *
  • javax.net.ssl.trustStoreType
  • *
  • javax.net.ssl.trustStore
  • *
  • javax.net.ssl.trustStoreProvider
  • *
  • javax.net.ssl.trustStorePassword
  • *
  • ssl.KeyManagerFactory.algorithm
  • *
  • javax.net.ssl.keyStoreType
  • *
  • javax.net.ssl.keyStore
  • *
  • javax.net.ssl.keyStoreProvider
  • *
  • javax.net.ssl.keyStorePassword
  • *
  • https.protocols
  • *
  • https.cipherSuites
  • *
* * @since 5.0 */ public class SSLConnectionSocketFactoryBuilder { public static SSLConnectionSocketFactoryBuilder create() { return new SSLConnectionSocketFactoryBuilder(); } private SSLContext sslContext; private String[] tlsVersions; private String[] ciphers; private HostnameVerifier hostnameVerifier; private boolean systemProperties; /** * Assigns {@link SSLContext} instance. */ public SSLConnectionSocketFactoryBuilder setSslContext(final SSLContext sslContext) { this.sslContext = sslContext; return this; } /** * Assigns enabled {@code TLS} versions. */ public final SSLConnectionSocketFactoryBuilder setTlsVersions(final String... tlslVersions) { this.tlsVersions = tlslVersions; return this; } /** * Assigns enabled {@code TLS} versions. */ public final SSLConnectionSocketFactoryBuilder setTlsVersions(final TLS... tlslVersions) { this.tlsVersions = new String[tlslVersions.length]; for (int i = 0; i < tlslVersions.length; i++) { this.tlsVersions[i] = tlslVersions[i].id; } return this; } /** * Assigns enabled ciphers. */ public final SSLConnectionSocketFactoryBuilder setCiphers(final String... ciphers) { this.ciphers = ciphers; return this; } /** * Assigns {@link HostnameVerifier} instance. */ public SSLConnectionSocketFactoryBuilder setHostnameVerifier(final HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; return this; } /** * Use system properties when creating and configuring default * implementations. */ public final SSLConnectionSocketFactoryBuilder useSystemProperties() { this.systemProperties = true; return this; } public SSLConnectionSocketFactory build() { final javax.net.ssl.SSLSocketFactory socketFactory; if (sslContext != null) { socketFactory = sslContext.getSocketFactory(); } else { if (systemProperties) { socketFactory = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(); } else { socketFactory = SSLContexts.createDefault().getSocketFactory(); } } final String[] tlsVersionsCopy; if (tlsVersions != null) { tlsVersionsCopy = tlsVersions; } else { tlsVersionsCopy = systemProperties ? HttpsSupport.getSystemProtocols() : null; } final String[] ciphersCopy; if (ciphers != null) { ciphersCopy = ciphers; } else { ciphersCopy = systemProperties ? HttpsSupport.getSystemCipherSuits() : null; } return new SSLConnectionSocketFactory( socketFactory, tlsVersionsCopy, ciphersCopy, hostnameVerifier != null ? hostnameVerifier : HttpsSupport.getDefaultHostnameVerifier()); } } SubjectName.java000066400000000000000000000036521434266521000344470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import org.apache.hc.core5.util.Args; final class SubjectName { static final int DNS = 2; static final int IP = 7; private final String value; private final int type; static SubjectName IP(final String value) { return new SubjectName(value, IP); } static SubjectName DNS(final String value) { return new SubjectName(value, DNS); } SubjectName(final String value, final int type) { this.value = Args.notNull(value, "Value"); this.type = Args.positive(type, "Type"); } public int getType() { return type; } public String getValue() { return value; } @Override public String toString() { return value; } } TlsSessionValidator.java000066400000000000000000000121221434266521000362130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; import org.slf4j.Logger; final class TlsSessionValidator { private final Logger log; TlsSessionValidator(final Logger log) { this.log = log; } void verifySession( final String hostname, final SSLSession sslsession, final HostnameVerifier hostnameVerifier) throws SSLException { if (log.isDebugEnabled()) { log.debug("Secure session established"); log.debug(" negotiated protocol: {}", sslsession.getProtocol()); log.debug(" negotiated cipher suite: {}", sslsession.getCipherSuite()); try { final Certificate[] certs = sslsession.getPeerCertificates(); final Certificate cert = certs[0]; if (cert instanceof X509Certificate) { final X509Certificate x509 = (X509Certificate) cert; final X500Principal peer = x509.getSubjectX500Principal(); log.debug(" peer principal: {}", peer); final Collection> altNames1 = x509.getSubjectAlternativeNames(); if (altNames1 != null) { final List altNames = new ArrayList<>(); for (final List aC : altNames1) { if (!aC.isEmpty()) { altNames.add(Objects.toString(aC.get(1), null)); } } log.debug(" peer alternative names: {}", altNames); } final X500Principal issuer = x509.getIssuerX500Principal(); log.debug(" issuer principal: {}", issuer); final Collection> altNames2 = x509.getIssuerAlternativeNames(); if (altNames2 != null) { final List altNames = new ArrayList<>(); for (final List aC : altNames2) { if (!aC.isEmpty()) { altNames.add(Objects.toString(aC.get(1), null)); } } log.debug(" issuer alternative names: {}", altNames); } } } catch (final Exception ignore) { } } if (hostnameVerifier != null) { final Certificate[] certs = sslsession.getPeerCertificates(); if (certs.length < 1) { throw new SSLPeerUnverifiedException("Peer certificate chain is empty"); } final Certificate peerCertificate = certs[0]; final X509Certificate x509Certificate; if (peerCertificate instanceof X509Certificate) { x509Certificate = (X509Certificate) peerCertificate; } else { throw new SSLPeerUnverifiedException("Unexpected certificate type: " + peerCertificate.getType()); } if (hostnameVerifier instanceof HttpClientHostnameVerifier) { ((HttpClientHostnameVerifier) hostnameVerifier).verify(hostname, x509Certificate); } else if (!hostnameVerifier.verify(hostname, sslsession)) { final List subjectAlts = DefaultHostnameVerifier.getSubjectAltNames(x509Certificate); throw new SSLPeerUnverifiedException("Certificate for <" + hostname + "> doesn't match any " + "of the subject alternative names: " + subjectAlts); } } } } TrustAllStrategy.java000066400000000000000000000037441434266521000355460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.ssl.TrustStrategy; /** * A trust strategy that accepts all certificates as trusted. Verification of * all other certificates is done by the trust manager configured in the SSL * context. * * @since 4.5.4 * @since 5.0 */ @Contract(threading = ThreadingBehavior.STATELESS) public class TrustAllStrategy implements TrustStrategy { public static final TrustAllStrategy INSTANCE = new TrustAllStrategy(); @Override public boolean isTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { return true; } } TrustSelfSignedStrategy.java000066400000000000000000000040101434266521000370440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.ssl.TrustStrategy; /** * A trust strategy that accepts self-signed certificates as trusted. Verification of all other * certificates is done by the trust manager configured in the SSL context. * * @since 4.1 */ @Contract(threading = ThreadingBehavior.STATELESS) public class TrustSelfSignedStrategy implements TrustStrategy { public static final TrustSelfSignedStrategy INSTANCE = new TrustSelfSignedStrategy(); @Override public boolean isTrusted( final X509Certificate[] chain, final String authType) throws CertificateException { return chain.length == 1; } } package-info.java000066400000000000000000000023541434266521000345710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client TLS/SSL support. */ package org.apache.hc.client5.http.ssl; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/000077500000000000000000000000001434266521000320145ustar00rootroot00000000000000Base64.java000066400000000000000000000124621434266521000336310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import static java.util.Base64.getEncoder; import static java.util.Base64.getMimeDecoder; import org.apache.hc.core5.annotation.Internal; /** * Provide implementations of the Base64 conversion methods from Commons Codec, delegating to the Java Base64 * implementation. *

* Only the features currently used by HttpClient are implemented here rather than all the features of Commons Codec. *

* Notes: *

    *
  • Commons Codec accepts null inputs, so this is also accepted here. This is not done in the Java 8 implementation
  • *
  • Decoding invalid input returns an empty value. The Java 8 implementation throws an exception, which is caught here
  • *
  • Commons Codec decoders accept both standard and url-safe variants of input. As this is not a requirement for * HttpClient, this is NOT implemented here. *
  • *
* This class is intended as in interim convenience. Any new code should use `java.util.Base64` directly. */ @Internal public class Base64 { private static final Base64 CODEC = new Base64(); private static final byte[] EMPTY_BYTES = new byte[0]; /** * Return an instance of the Base64 codec that use the regular Base64 alphabet * (as opposed to the URL-safe alphabet). Note that unlike the Commons Codec version, * thus class will NOT decode characters from URL-safe alphabet. */ public Base64() { } /** * Creates a Base64 codec used for decoding and encoding in URL-unsafe mode. *

* As HttpClient never uses a non-zero length, this feature is not implemented here. */ public Base64(final int lineLength) { if (lineLength != 0) { throw new UnsupportedOperationException("Line breaks not supported"); } } /** * Decodes Base64 data into octets. *

* Note: this method does NOT accept URL-safe encodings */ public static byte[] decodeBase64(final byte[] base64) { return CODEC.decode(base64); } /** * Decodes a Base64 String into octets. *

* Note: this method does NOT accept URL-safe encodings */ public static byte[] decodeBase64(final String base64) { return CODEC.decode(base64); } /** * Encodes binary data using the base64 algorithm but does not chunk the output. */ public static byte[] encodeBase64(final byte[] base64) { return CODEC.encode(base64); } /** * Encodes binary data using the base64 algorithm but does not chunk the output. */ public static String encodeBase64String(final byte[] bytes) { if (null == bytes) { return null; } return getEncoder().encodeToString(bytes); } /** * Decode Base64-encoded bytes to their original form, using specifications from this codec instance */ public byte[] decode(final byte[] base64) { if (null == base64) { return null; } try { return getMimeDecoder().decode(base64); } catch (final IllegalArgumentException e) { return EMPTY_BYTES; } } /** * Decode a Base64 String to its original form, using specifications from this codec instance */ public byte[] decode(final String base64) { if (null == base64) { return null; } try { // getMimeDecoder is used instead of getDecoder as it better matches the // functionality of the default Commons Codec implementation (primarily more forgiving of strictly // invalid inputs to decode) // Code using java.util.Base64 directly should make a choice based on whether this forgiving nature is // appropriate. return getMimeDecoder().decode(base64); } catch (final IllegalArgumentException e) { return EMPTY_BYTES; } } /** * Encode bytes to their Base64 form, using specifications from this codec instance */ public byte[] encode(final byte[] value) { if (null == value) { return null; } return getEncoder().encode(value); } } ByteArrayBuilder.java000066400000000000000000000151171434266521000360160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; /** * Builder class for sequences of bytes. * * @since 5.0 */ public final class ByteArrayBuilder { private CharsetEncoder charsetEncoder; private ByteBuffer buffer; public ByteArrayBuilder() { } public ByteArrayBuilder(final int initialCapacity) { this.buffer = ByteBuffer.allocate(initialCapacity); } public int capacity() { return this.buffer != null ? this.buffer.capacity() : 0; } static ByteBuffer ensureFreeCapacity(final ByteBuffer buffer, final int capacity) { if (buffer == null) { return ByteBuffer.allocate(capacity); } if (buffer.remaining() < capacity) { final ByteBuffer newBuffer = ByteBuffer.allocate(buffer.position() + capacity); buffer.flip(); newBuffer.put(buffer); return newBuffer; } return buffer; } static ByteBuffer encode( final ByteBuffer buffer, final CharBuffer in, final CharsetEncoder encoder) throws CharacterCodingException { final int capacity = (int) (in.remaining() * encoder.averageBytesPerChar()); ByteBuffer out = ensureFreeCapacity(buffer, capacity); while (in.hasRemaining()) { CoderResult result = encoder.encode(in, out, true); if (result.isError()) { result.throwException(); } if (result.isUnderflow()) { result = encoder.flush(out); } if (result.isUnderflow()) { break; } if (result.isOverflow()) { out = ensureFreeCapacity(out, capacity); } } return out; } public void ensureFreeCapacity(final int freeCapacity) { this.buffer = ensureFreeCapacity(this.buffer, freeCapacity); } private void doAppend(final CharBuffer charBuffer) { if (this.charsetEncoder == null) { this.charsetEncoder = StandardCharsets.US_ASCII.newEncoder() .onMalformedInput(CodingErrorAction.IGNORE) .onUnmappableCharacter(CodingErrorAction.REPLACE); } this.charsetEncoder.reset(); try { this.buffer = encode(this.buffer, charBuffer, this.charsetEncoder); } catch (final CharacterCodingException ex) { // Should never happen throw new IllegalStateException("Unexpected character coding error", ex); } } public ByteArrayBuilder charset(final Charset charset) { if (charset == null) { this.charsetEncoder = null; } else { this.charsetEncoder = charset.newEncoder() .onMalformedInput(CodingErrorAction.IGNORE) .onUnmappableCharacter(CodingErrorAction.REPLACE); } return this; } public ByteArrayBuilder append(final byte[] b, final int off, final int len) { if (b == null) { return this; } if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) < 0) || ((off + len) > b.length)) { throw new IndexOutOfBoundsException("off: " + off + " len: " + len + " b.length: " + b.length); } ensureFreeCapacity(len); this.buffer.put(b, off, len); return this; } public ByteArrayBuilder append(final byte[] b) { if (b == null) { return this; } return append(b, 0, b.length); } public ByteArrayBuilder append(final CharBuffer charBuffer) { if (charBuffer == null) { return this; } doAppend(charBuffer); return this; } public ByteArrayBuilder append(final char[] b, final int off, final int len) { if (b == null) { return this; } if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) < 0) || ((off + len) > b.length)) { throw new IndexOutOfBoundsException("off: " + off + " len: " + len + " b.length: " + b.length); } return append(CharBuffer.wrap(b, off, len)); } public ByteArrayBuilder append(final char[] b) { if (b == null) { return this; } return append(b, 0, b.length); } public ByteArrayBuilder append(final String s) { if (s == null) { return this; } return append(CharBuffer.wrap(s)); } public ByteBuffer toByteBuffer() { return this.buffer != null ? this.buffer.duplicate() : ByteBuffer.allocate(0); } public byte[] toByteArray() { if (this.buffer == null) { return new byte[] {}; } this.buffer.flip(); final byte[] b = new byte[this.buffer.remaining()]; this.buffer.get(b); this.buffer.clear(); return b; } public void reset() { if (this.charsetEncoder != null) { this.charsetEncoder.reset(); } if (this.buffer != null) { this.buffer.clear(); } } @Override public String toString() { return this.buffer != null ? this.buffer.toString() : "null"; } } DateUtils.java000066400000000000000000000354441434266521000345100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.MessageHeaders; import org.apache.hc.core5.util.Args; /** * A utility class for parsing and formatting HTTP dates as used in cookies and * other headers. This class handles dates as defined by RFC 2616 section * 3.3.1 as well as some other common non-standard formats. * * @since 4.3 */ public final class DateUtils { /** * Date format pattern used to parse HTTP date headers in RFC 1123 format. */ public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; /** * Date formatter used to parse HTTP date headers in RFC 1123 format. * * @since 5.2 */ public static final DateTimeFormatter FORMATTER_RFC1123 = new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(PATTERN_RFC1123) .toFormatter(Locale.ENGLISH); /** * Date format pattern used to parse HTTP date headers in RFC 1036 format. */ public static final String PATTERN_RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz"; /** * Date formatter used to parse HTTP date headers in RFC 1036 format. * * @since 5.2 */ public static final DateTimeFormatter FORMATTER_RFC1036 = new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(PATTERN_RFC1036) .toFormatter(Locale.ENGLISH); /** * Date format pattern used to parse HTTP date headers in ANSI C * {@code asctime()} format. */ public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy"; /** * Date formatter used to parse HTTP date headers in in ANSI C {@code asctime()} format. * * @since 5.2 */ public static final DateTimeFormatter FORMATTER_ASCTIME = new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(PATTERN_ASCTIME) .toFormatter(Locale.ENGLISH); /** * Standard date formatters: {@link #FORMATTER_RFC1123}, {@link #FORMATTER_RFC1036}, {@link #FORMATTER_ASCTIME}. * * @since 5.2 */ public static final DateTimeFormatter[] STANDARD_PATTERNS = new DateTimeFormatter[] { FORMATTER_RFC1123, FORMATTER_RFC1036, FORMATTER_ASCTIME }; static final ZoneId GMT_ID = ZoneId.of("GMT"); /** * @since 5.2 */ public static Date toDate(final Instant instant) { return instant != null ? new Date(instant.toEpochMilli()) : null; } /** * @since 5.2 */ public static Instant toInstant(final Date date) { return date != null ? Instant.ofEpochMilli(date.getTime()) : null; } /** * @since 5.2 */ public static LocalDateTime toUTC(final Instant instant) { return instant != null ? instant.atZone(ZoneOffset.UTC).toLocalDateTime() : null; } /** * @since 5.2 */ public static LocalDateTime toUTC(final Date date) { return toUTC(toInstant(date)); } /** * Parses the date value using the given date/time formats. * * @param dateValue the instant value to parse * @param dateFormatters the date/time formats to use * * @return the parsed instant or null if input could not be parsed * * @since 5.2 */ public static Instant parseDate(final String dateValue, final DateTimeFormatter... dateFormatters) { Args.notNull(dateValue, "Date value"); String v = dateValue; // trim single quotes around date if present // see issue #5279 if (v.length() > 1 && v.startsWith("'") && v.endsWith("'")) { v = v.substring (1, v.length() - 1); } for (final DateTimeFormatter dateFormatter : dateFormatters) { try { return Instant.from(dateFormatter.parse(v)); } catch (final DateTimeParseException ignore) { } } return null; } /** * Parses the instant value using the standard date/time formats ({@link #PATTERN_RFC1123}, * {@link #PATTERN_RFC1036}, {@link #PATTERN_ASCTIME}). * * @param dateValue the instant value to parse * * @return the parsed instant or null if input could not be parsed * * @since 5.2 */ public static Instant parseStandardDate(final String dateValue) { return parseDate(dateValue, STANDARD_PATTERNS); } /** * Parses an instant value from a header with the given name. * * @param headers message headers * @param headerName header name * * @return the parsed instant or null if input could not be parsed * * @since 5.2 */ public static Instant parseStandardDate(final MessageHeaders headers, final String headerName) { if (headers == null) { return null; } final Header header = headers.getFirstHeader(headerName); if (header == null) { return null; } return parseStandardDate(header.getValue()); } /** * Formats the given instant according to the RFC 1123 pattern. * * @param instant Instant to format. * @return An RFC 1123 formatted instant string. * * @see #PATTERN_RFC1123 * * @since 5.2 */ public static String formatStandardDate(final Instant instant) { return formatDate(instant, FORMATTER_RFC1123); } /** * Formats the given date according to the specified pattern. * * @param instant Instant to format. * @param dateTimeFormatter The pattern to use for formatting the instant. * @return A formatted instant string. * * @throws IllegalArgumentException If the given date pattern is invalid. * * @since 5.2 */ public static String formatDate(final Instant instant, final DateTimeFormatter dateTimeFormatter) { Args.notNull(instant, "Instant"); Args.notNull(dateTimeFormatter, "DateTimeFormatter"); return dateTimeFormatter.format(instant.atZone(GMT_ID)); } /** * @deprecated This attribute is no longer supported as a part of the public API. */ @Deprecated public static final TimeZone GMT = TimeZone.getTimeZone("GMT"); /** * Parses a date value. The formats used for parsing the date value are retrieved from * the default http params. * * @param dateValue the date value to parse * * @return the parsed date or null if input could not be parsed * * @deprecated Use {@link #parseStandardDate(String)} */ @Deprecated public static Date parseDate(final String dateValue) { return parseDate(dateValue, null, null); } /** * Parses a date value from a header with the given name. * * @param headers message headers * @param headerName header name * * @return the parsed date or null if input could not be parsed * * @since 5.0 * * @deprecated Use {@link #parseStandardDate(MessageHeaders, String)} */ @Deprecated public static Date parseDate(final MessageHeaders headers, final String headerName) { return toDate(parseStandardDate(headers, headerName)); } /** * Tests if the first message is after (newer) than second one * using the given message header for comparison. * * @param message1 the first message * @param message2 the second message * @param headerName header name * * @return {@code true} if both messages contain a header with the given name * and the value of the header from the first message is newer that of * the second message. * * @since 5.0 * * @deprecated This method is no longer supported as a part of the public API. */ @Deprecated public static boolean isAfter( final MessageHeaders message1, final MessageHeaders message2, final String headerName) { if (message1 != null && message2 != null) { final Header dateHeader1 = message1.getFirstHeader(headerName); if (dateHeader1 != null) { final Header dateHeader2 = message2.getFirstHeader(headerName); if (dateHeader2 != null) { final Date date1 = parseDate(dateHeader1.getValue()); if (date1 != null) { final Date date2 = parseDate(dateHeader2.getValue()); if (date2 != null) { return date1.after(date2); } } } } } return false; } /** * Tests if the first message is before (older) than the second one * using the given message header for comparison. * * @param message1 the first message * @param message2 the second message * @param headerName header name * * @return {@code true} if both messages contain a header with the given name * and the value of the header from the first message is older that of * the second message. * * @since 5.0 * * @deprecated This method is no longer supported as a part of the public API. */ @Deprecated public static boolean isBefore( final MessageHeaders message1, final MessageHeaders message2, final String headerName) { if (message1 != null && message2 != null) { final Header dateHeader1 = message1.getFirstHeader(headerName); if (dateHeader1 != null) { final Header dateHeader2 = message2.getFirstHeader(headerName); if (dateHeader2 != null) { final Date date1 = parseDate(dateHeader1.getValue()); if (date1 != null) { final Date date2 = parseDate(dateHeader2.getValue()); if (date2 != null) { return date1.before(date2); } } } } } return false; } /** * Parses the date value using the given date/time formats. * * @param dateValue the date value to parse * @param dateFormats the date/time formats to use * * @return the parsed date or null if input could not be parsed * * @deprecated Use {@link #parseDate(String, DateTimeFormatter...)} */ @Deprecated public static Date parseDate(final String dateValue, final String[] dateFormats) { return parseDate(dateValue, dateFormats, null); } /** * Parses the date value using the given date/time formats. * * @param dateValue the date value to parse * @param dateFormats the date/time formats to use * @param startDate During parsing, two digit years will be placed in the range * {@code startDate} to {@code startDate + 100 years}. This value may * be {@code null}. When {@code null} is given as a parameter, year * {@code 2000} will be used. * * @return the parsed date or null if input could not be parsed * * @deprecated Use {@link #parseDate(String, DateTimeFormatter...)} */ @Deprecated public static Date parseDate( final String dateValue, final String[] dateFormats, final Date startDate) { final DateTimeFormatter[] dateTimeFormatters; if (dateFormats != null) { dateTimeFormatters = new DateTimeFormatter[dateFormats.length]; for (int i = 0; i < dateFormats.length; i++) { dateTimeFormatters[i] = new DateTimeFormatterBuilder() .parseLenient() .parseCaseInsensitive() .appendPattern(dateFormats[i]) .toFormatter(); } } else { dateTimeFormatters = STANDARD_PATTERNS; } return toDate(parseDate(dateValue, dateTimeFormatters)); } /** * Formats the given date according to the RFC 1123 pattern. * * @param date The date to format. * @return An RFC 1123 formatted date string. * * @see #PATTERN_RFC1123 * * @deprecated Use {@link #formatStandardDate(Instant)} */ @Deprecated public static String formatDate(final Date date) { return formatStandardDate(toInstant(date)); } /** * Formats the given date according to the specified pattern. * * @param date The date to format. * @param pattern The pattern to use for formatting the date. * @return A formatted date string. * * @throws IllegalArgumentException If the given date pattern is invalid. * * @deprecated Use {@link #formatDate(Instant, DateTimeFormatter)} */ @Deprecated public static String formatDate(final Date date, final String pattern) { Args.notNull(date, "Date"); Args.notNull(pattern, "Pattern"); return DateTimeFormatter.ofPattern(pattern).format(toInstant(date).atZone(GMT_ID)); } /** * Clears thread-local variable containing {@link java.text.DateFormat} cache. * * @since 4.3 * * @deprecated Noop method. Do not use. */ @Deprecated public static void clearThreadLocal() { } /** This class should not be instantiated. */ private DateUtils() { } } DnsUtils.java000066400000000000000000000044511434266521000343510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; /** * A collection of utilities relating to Domain Name System. * * @since 4.5 */ public class DnsUtils { private DnsUtils() { } private static boolean isUpper(final char c) { return c >= 'A' && c <= 'Z'; } public static String normalize(final String s) { if (s == null) { return null; } int pos = 0; int remaining = s.length(); while (remaining > 0) { if (isUpper(s.charAt(pos))) { break; } pos++; remaining--; } if (remaining > 0) { final StringBuilder buf = new StringBuilder(s.length()); buf.append(s, 0, pos); while (remaining > 0) { final char c = s.charAt(pos); if (isUpper(c)) { buf.append((char) (c + ('a' - 'A'))); } else { buf.append(c); } pos++; remaining--; } return buf.toString(); } else { return s; } } } httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/Hex.java000066400000000000000000000063351434266521000334120ustar00rootroot00000000000000/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import org.apache.hc.core5.annotation.Internal; @Internal public class Hex { private Hex() { } public static String encodeHexString(final byte[] bytes) { final char[] out = new char[bytes.length * 2]; encodeHex(bytes, 0, bytes.length, DIGITS_LOWER, out, 0); return new String(out); } // // The following comes from commons-codec // https://github.com/apache/commons-codec/blob/master/src/main/java/org/apache/commons/codec/binary/Hex.java /** * Used to build output as hex. */ private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. * * @param data a byte[] to convert to hex characters * @param dataOffset the position in {@code data} to start encoding from * @param dataLen the number of bytes from {@code dataOffset} to encode * @param toDigits the output alphabet (must contain at least 16 chars) * @param out a char[] which will hold the resultant appropriate characters from the alphabet. * @param outOffset the position within {@code out} at which to start writing the encoded characters. */ private static void encodeHex(final byte[] data, final int dataOffset, final int dataLen, final char[] toDigits, final char[] out, final int outOffset) { // two characters form the hex value. for (int i = dataOffset, j = outOffset; i < dataOffset + dataLen; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } } /* // Can be replaced in Java 17 with the following: private static final java.util.HexFormat HEX_FORMAT = HexFormat.of(); public static String encodeHex(byte[] bytes) { return HEX_FORMAT.formatHex(bytes); } */ } URIUtils.java000066400000000000000000000274611434266521000342720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.net.URIAuthority; import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TextUtils; /** * A collection of utilities for {@link URI URIs}, to workaround * bugs within the class or for ease-of-use features. * * @since 4.0 */ public class URIUtils { /** * A convenience method for creating a new {@link URI} whose scheme, host * and port are taken from the target host, but whose path, query and * fragment are taken from the existing URI. The fragment is only used if * dropFragment is false. The path is set to "/" if not explicitly specified. * * @param uri * Contains the path, query and fragment to use. * @param target * Contains the scheme, host and port to use. * @param dropFragment * True if the fragment should not be copied. * * @throws URISyntaxException * If the resulting URI is invalid. * * @deprecated Use {@link URIBuilder}. */ @Deprecated public static URI rewriteURI( final URI uri, final HttpHost target, final boolean dropFragment) throws URISyntaxException { Args.notNull(uri, "URI"); if (uri.isOpaque()) { return uri; } final URIBuilder uribuilder = new URIBuilder(uri); if (target != null) { uribuilder.setScheme(target.getSchemeName()); uribuilder.setHost(target.getHostName()); uribuilder.setPort(target.getPort()); } else { uribuilder.setScheme(null); uribuilder.setHost((String) null); uribuilder.setPort(-1); } if (dropFragment) { uribuilder.setFragment(null); } final List originalPathSegments = uribuilder.getPathSegments(); final List pathSegments = new ArrayList<>(originalPathSegments); for (final Iterator it = pathSegments.iterator(); it.hasNext(); ) { final String pathSegment = it.next(); if (pathSegment.isEmpty() && it.hasNext()) { it.remove(); } } if (pathSegments.size() != originalPathSegments.size()) { uribuilder.setPathSegments(pathSegments); } if (pathSegments.isEmpty()) { uribuilder.setPathSegments(""); } return uribuilder.build(); } /** * A convenience method for * {@link URIUtils#rewriteURI(URI, HttpHost, boolean)} that always keeps the * fragment. * * @deprecated Use {@link URIBuilder}. */ @Deprecated public static URI rewriteURI( final URI uri, final HttpHost target) throws URISyntaxException { return rewriteURI(uri, target, false); } /** * A convenience method that creates a new {@link URI} whose scheme, host, port, path, * query are taken from the existing URI, dropping any fragment or user-information. * The path is set to "/" if not explicitly specified. The existing URI is returned * unmodified if it has no fragment or user-information and has a path. * * @param uri * original URI. * @throws URISyntaxException * If the resulting URI is invalid. * * @deprecated Use {@link URIBuilder}. */ @Deprecated public static URI rewriteURI(final URI uri) throws URISyntaxException { Args.notNull(uri, "URI"); if (uri.isOpaque()) { return uri; } final URIBuilder uribuilder = new URIBuilder(uri); if (uribuilder.getUserInfo() != null) { uribuilder.setUserInfo(null); } if (uribuilder.isPathEmpty()) { uribuilder.setPathSegments(""); } if (uribuilder.getHost() != null) { uribuilder.setHost(uribuilder.getHost().toLowerCase(Locale.ROOT)); } uribuilder.setFragment(null); return uribuilder.build(); } /** * Resolves a URI reference against a base URI. Work-around for bug in * java.net.URI (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535) * * @param baseURI the base URI * @param reference the URI reference * @return the resulting URI */ public static URI resolve(final URI baseURI, final String reference) { return resolve(baseURI, URI.create(reference)); } /** * Resolves a URI reference against a base URI. Work-around for bugs in * java.net.URI (e.g. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535) * * @param baseURI the base URI * @param reference the URI reference * @return the resulting URI */ public static URI resolve(final URI baseURI, final URI reference) { Args.notNull(baseURI, "Base URI"); Args.notNull(reference, "Reference URI"); final String s = reference.toASCIIString(); if (s.startsWith("?")) { String baseUri = baseURI.toASCIIString(); final int i = baseUri.indexOf('?'); baseUri = i > -1 ? baseUri.substring(0, i) : baseUri; return URI.create(baseUri + s); } final boolean emptyReference = s.isEmpty(); URI resolved; if (emptyReference) { resolved = baseURI.resolve(URI.create("#")); final String resolvedString = resolved.toASCIIString(); resolved = URI.create(resolvedString.substring(0, resolvedString.indexOf('#'))); } else { resolved = baseURI.resolve(reference); } try { return normalizeSyntax(resolved); } catch (final URISyntaxException ex) { throw new IllegalArgumentException(ex); } } /** * Removes dot segments and performs Syntax-Based Normalization. * * @param uri the original URI * @return the URI without dot segments */ static URI normalizeSyntax(final URI uri) throws URISyntaxException { if (uri.isOpaque() || uri.getAuthority() == null) { // opaque and file: URIs return uri; } final URIBuilder builder = new URIBuilder(uri); builder.normalizeSyntax(); if (builder.getScheme() == null) { builder.setScheme(URIScheme.HTTP.id); } if (builder.isPathEmpty()) { builder.setPathSegments(""); } return builder.build(); } /** * Extracts target host from the given {@link URI}. * * @param uri * @return the target host if the URI is absolute or {@code null} if the URI is * relative or does not contain a valid host name. * * @since 4.1 */ public static HttpHost extractHost(final URI uri) { if (uri == null) { return null; } final URIBuilder uriBuilder = new URIBuilder(uri); final String scheme = uriBuilder.getScheme(); final String host = uriBuilder.getHost(); final int port = uriBuilder.getPort(); if (!TextUtils.isBlank(host)) { try { return new HttpHost(scheme, host, port); } catch (final IllegalArgumentException ignore) { } } return null; } /** * Derives the interpreted (absolute) URI that was used to generate the last * request. This is done by extracting the request-uri and target origin for * the last request and scanning all the redirect locations for the last * fragment identifier, then combining the result into a {@link URI}. * * @param originalURI * original request before any redirects * @param target * if the last URI is relative, it is resolved against this target, * or {@code null} if not available. * @param redirects * collection of redirect locations since the original request * or {@code null} if not available. * @return interpreted (absolute) URI */ public static URI resolve( final URI originalURI, final HttpHost target, final List redirects) throws URISyntaxException { Args.notNull(originalURI, "Request URI"); final URIBuilder uribuilder; if (redirects == null || redirects.isEmpty()) { uribuilder = new URIBuilder(originalURI); } else { uribuilder = new URIBuilder(redirects.get(redirects.size() - 1)); String frag = uribuilder.getFragment(); // read interpreted fragment identifier from redirect locations for (int i = redirects.size() - 1; frag == null && i >= 0; i--) { frag = redirects.get(i).getFragment(); } uribuilder.setFragment(frag); } // read interpreted fragment identifier from original request if (uribuilder.getFragment() == null) { uribuilder.setFragment(originalURI.getFragment()); } // last target origin if (target != null && !uribuilder.isAbsolute()) { uribuilder.setScheme(target.getSchemeName()); uribuilder.setHost(target.getHostName()); uribuilder.setPort(target.getPort()); } return uribuilder.build(); } /** * Convenience factory method for {@link URI} instances. * * @since 5.0 * * @deprecated Use {@link URIBuilder}. */ @Deprecated public static URI create(final HttpHost host, final String path) throws URISyntaxException { final URIBuilder builder = new URIBuilder(path); if (host != null) { builder.setHost(host.getHostName()).setPort(host.getPort()).setScheme(host.getSchemeName()); } return builder.build(); } /** * Convenience factory method for {@link URI} instances. * * @since 5.0 * * @deprecated Use {@link URIBuilder}. */ @Deprecated public static URI create(final String scheme, final URIAuthority host, final String path) throws URISyntaxException { final URIBuilder builder = new URIBuilder(path); if (scheme != null) { builder.setScheme(scheme); } if (host != null) { builder.setHost(host.getHostName()).setPort(host.getPort()); } return builder.build(); } /** * This class should not be instantiated. */ private URIUtils() { } } package-info.java000066400000000000000000000023561434266521000351320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ /** * Client utility classes. */ package org.apache.hc.client5.http.utils; httpcomponents-client-rel-v5.2.1/httpclient5/src/main/resources/000077500000000000000000000000001434266521000250215ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/resources/org/000077500000000000000000000000001434266521000256105ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/resources/org/apache/000077500000000000000000000000001434266521000270315ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/resources/org/apache/hc/000077500000000000000000000000001434266521000274235ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/resources/org/apache/hc/client5/000077500000000000000000000000001434266521000307665ustar00rootroot00000000000000version.properties000066400000000000000000000015401434266521000345120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/main/resources/org/apache/hc/client5# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT 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 = ${project.artifactId} info.release = ${project.version} httpcomponents-client-rel-v5.2.1/httpclient5/src/test/000077500000000000000000000000001434266521000230425ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/000077500000000000000000000000001434266521000237635ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/000077500000000000000000000000001434266521000245525ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/000077500000000000000000000000001434266521000257735ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/000077500000000000000000000000001434266521000263655ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/000077500000000000000000000000001434266521000277305ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/000077500000000000000000000000001434266521000307075ustar00rootroot00000000000000ConnectExceptionSupportTest.java000066400000000000000000000114401434266521000372000ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.io.IOException; import java.net.InetAddress; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for exceptions. * Trivial, but it looks better in the Clover reports. */ public class ConnectExceptionSupportTest { @Test public void testConnectTimeoutExceptionFromNullMessageAndHost() { final ConnectTimeoutException ctx = ConnectExceptionSupport.createConnectTimeoutException(null, null); Assertions.assertEquals("Connect to remote endpoint timed out", ctx.getMessage()); } @Test public void testConnectTimeoutExceptionFromCause() { final IOException cause = new IOException("something awful"); final ConnectTimeoutException ctx = ConnectExceptionSupport.createConnectTimeoutException(cause, null); Assertions.assertEquals("Connect to remote endpoint failed: something awful", ctx.getMessage()); } @Test public void testConnectTimeoutExceptionFromCauseAndHost() { final HttpHost target = new HttpHost("localhost"); final IOException cause = new IOException(); final ConnectTimeoutException ctx = ConnectExceptionSupport.createConnectTimeoutException(cause, target); Assertions.assertEquals("Connect to http://localhost timed out", ctx.getMessage()); } @Test public void testConnectTimeoutExceptionFromCauseHostAndRemoteAddress() throws Exception { final HttpHost target = new HttpHost("localhost"); final InetAddress remoteAddress = InetAddress.getByAddress(new byte[] {1,2,3,4}); final IOException cause = new IOException(); final ConnectTimeoutException ctx = ConnectExceptionSupport.createConnectTimeoutException(cause, target, remoteAddress); Assertions.assertEquals("Connect to http://localhost [/1.2.3.4] timed out", ctx.getMessage()); } @Test public void testHttpHostConnectExceptionFromNullCause() { final HttpHostConnectException ctx = ConnectExceptionSupport.createHttpHostConnectException(null, null, (InetAddress [])null); Assertions.assertEquals("Connect to remote endpoint refused", ctx.getMessage()); } @Test public void testHttpHostConnectExceptionFromCause() { final IOException cause = new IOException("something awful"); final HttpHostConnectException ctx = ConnectExceptionSupport.createHttpHostConnectException(cause, null); Assertions.assertEquals("Connect to remote endpoint failed: something awful", ctx.getMessage()); } @Test public void testHttpHostConnectExceptionFromCauseAndHost() { final HttpHost target = new HttpHost("localhost"); final IOException cause = new IOException(); final HttpHostConnectException ctx = ConnectExceptionSupport.createHttpHostConnectException(cause, target); Assertions.assertEquals("Connect to http://localhost refused", ctx.getMessage()); } @Test public void testHttpHostConnectExceptionFromCauseHostAndRemoteAddress() throws Exception { final HttpHost target = new HttpHost("localhost"); final InetAddress remoteAddress1 = InetAddress.getByAddress(new byte[] {1,2,3,4}); final InetAddress remoteAddress2 = InetAddress.getByAddress(new byte[] {5,6,7,8}); final IOException cause = new IOException(); final HttpHostConnectException ctx = ConnectExceptionSupport.createHttpHostConnectException(cause, target, remoteAddress1, remoteAddress2); Assertions.assertEquals("Connect to http://localhost [/1.2.3.4, /5.6.7.8] refused", ctx.getMessage()); } } ContentTypeMatcher.java000066400000000000000000000042161434266521000352560ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import org.apache.hc.core5.http.ContentType; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class ContentTypeMatcher extends BaseMatcher { private final ContentType expectedContentType; public ContentTypeMatcher(final ContentType contentType) { this.expectedContentType = contentType; } @Override public boolean matches(final Object item) { if (item instanceof ContentType) { final ContentType contentType = (ContentType) item; return contentType.isSameMimeType(expectedContentType); } return false; } @Override public void describeTo(final Description description) { description.appendText("same MIME type as ").appendValue(expectedContentType); } public static Matcher sameMimeType(final ContentType contentType) { return new ContentTypeMatcher(contentType); } } HeaderMatcher.java000066400000000000000000000044501434266521000341720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.util.Objects; import org.apache.hc.core5.http.Header; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class HeaderMatcher extends BaseMatcher

{ private final String headerName; private final Object headerValue; public HeaderMatcher(final String headerName, final Object headerValue) { this.headerName = headerName; this.headerValue = headerValue; } @Override public boolean matches(final Object item) { if (item instanceof Header) { final Header header = (Header) item; return headerName.equalsIgnoreCase(header.getName()) && Objects.equals(headerValue, header.getValue()); } return false; } @Override public void describeTo(final Description description) { description.appendText("same header as ").appendValue(headerValue).appendText(": ").appendValue(headerValue); } public static Matcher
same(final String headerName, final Object headerValue) { return new HeaderMatcher(headerName, headerValue); } } HeadersMatcher.java000066400000000000000000000050311434266521000343510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.util.Objects; import org.apache.hc.core5.http.Header; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class HeadersMatcher extends BaseMatcher { private final Header[] expectedHeaders; public HeadersMatcher(final Header... headers) { this.expectedHeaders = headers; } @Override public boolean matches(final Object item) { if (item instanceof Header[]) { final Header[] headers = (Header[]) item; if (headers.length == expectedHeaders.length) { for (int i = 0; i < headers.length; i++) { final Header h1 = headers[i]; final Header h2 = expectedHeaders[i]; if (!h1.getName().equalsIgnoreCase(h2.getName()) || !Objects.equals(h1.getValue(), h2.getValue())) { return false; } } return true; } } return false; } @Override public void describeTo(final Description description) { description.appendText("same headers as ").appendValueList("[", ", ", "]", expectedHeaders); } public static Matcher same(final Header... headers) { return new HeadersMatcher(headers); } } NameValuePairMatcher.java000066400000000000000000000046111434266521000354720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.util.Objects; import org.apache.hc.core5.http.NameValuePair; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class NameValuePairMatcher extends BaseMatcher { private final String name; private final String value; public NameValuePairMatcher(final String name, final String value) { this.name = name; this.value = value; } @Override public boolean matches(final Object item) { if (item instanceof NameValuePair) { final NameValuePair nvp = (NameValuePair) item; return Objects.equals(nvp.getName(), name) && Objects.equals(nvp.getValue(), value); } return false; } @Override public void describeTo(final Description description) { description.appendText("equals ").appendValue(name).appendText("=").appendValue(value); } public static Matcher equals(final String name, final String value) { return new NameValuePairMatcher(name, value); } public static Matcher same(final String name, final String value) { return new NameValuePairMatcher(name, value); } } NameValuePairsMatcher.java000066400000000000000000000063031434266521000356550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Objects; import org.apache.hc.core5.http.NameValuePair; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class NameValuePairsMatcher extends BaseMatcher> { private final List expectedNameValuePairList; public NameValuePairsMatcher(final List nameValuePairList) { this.expectedNameValuePairList = nameValuePairList; } @Override public boolean matches(final Object item) { if (item instanceof Collection) { final Collection collection = (Collection) item; int i = 0; for (final Object obj : collection) { if (obj instanceof NameValuePair) { final NameValuePair nvp1 = (NameValuePair) obj; final NameValuePair nvp2 = expectedNameValuePairList.get(i); if (!nvp1.getName().equalsIgnoreCase(nvp2.getName()) || !Objects.equals(nvp1.getValue(), nvp2.getValue())) { return false; } } else { return false; } i++; } return true; } return false; } @Override public void describeTo(final Description description) { description.appendText("same name/value pairs as ").appendValueList("[", ", ", "]", expectedNameValuePairList); } public static Matcher> same(final Collection nameValuePairs) { return new NameValuePairsMatcher(new ArrayList<>(nameValuePairs)); } public static Matcher> same(final NameValuePair... nameValuePairs) { return new NameValuePairsMatcher(Arrays.asList(nameValuePairs)); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/async/000077500000000000000000000000001434266521000320245ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/async/methods/000077500000000000000000000000001434266521000334675ustar00rootroot00000000000000TestSimpleMessageBuilders.java000066400000000000000000000344401434266521000413500ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/async/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.async.methods; import static org.hamcrest.MatcherAssert.assertThat; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.hc.client5.http.ContentTypeMatcher; import org.apache.hc.client5.http.HeaderMatcher; import org.apache.hc.client5.http.HeadersMatcher; import org.apache.hc.client5.http.NameValuePairsMatcher; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.hc.core5.net.URIAuthority; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link SimpleResponseBuilder} and {@link SimpleRequestBuilder}. */ public class TestSimpleMessageBuilders { @Test public void testResponseBasics() throws Exception { final SimpleResponseBuilder builder = SimpleResponseBuilder.create(200); Assertions.assertEquals(200, builder.getStatus()); Assertions.assertNull(builder.getHeaders()); Assertions.assertNull(builder.getVersion()); final SimpleHttpResponse r1 = builder.build(); Assertions.assertNotNull(r1); Assertions.assertEquals(200, r1.getCode()); Assertions.assertNull(r1.getVersion()); builder.setStatus(500); builder.setVersion(HttpVersion.HTTP_1_0); Assertions.assertEquals(500, builder.getStatus()); Assertions.assertEquals(HttpVersion.HTTP_1_0, builder.getVersion()); final SimpleHttpResponse r2 = builder.build(); Assertions.assertEquals(500, r2.getCode()); Assertions.assertEquals(HttpVersion.HTTP_1_0, r2.getVersion()); builder.addHeader("h1", "v1"); builder.addHeader("h1", "v2"); builder.addHeader("h2", "v2"); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(builder.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(builder.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(builder.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); final SimpleHttpResponse r3 = builder.build(); assertThat(r3.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(r3.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(r3.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(r3.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); builder.removeHeader(new BasicHeader("h1", "v2")); assertThat(builder.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); final SimpleHttpResponse r4 = builder.build(); assertThat(r4.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(r4.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); builder.removeHeaders("h1"); assertThat(builder.getHeaders("h1"), HeadersMatcher.same()); assertThat(builder.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); final SimpleHttpResponse r5 = builder.build(); assertThat(r5.getHeaders("h1"), HeadersMatcher.same()); assertThat(r5.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); } @Test public void testRequestBasics() throws Exception { final SimpleRequestBuilder builder = SimpleRequestBuilder.get(); Assertions.assertEquals(URI.create("/"), builder.getUri()); Assertions.assertEquals("GET", builder.getMethod()); Assertions.assertNull(builder.getScheme()); Assertions.assertNull(builder.getAuthority()); Assertions.assertNull(builder.getPath()); Assertions.assertNull(builder.getHeaders()); Assertions.assertNull(builder.getVersion()); Assertions.assertNull(builder.getCharset()); Assertions.assertNull(builder.getParameters()); final SimpleHttpRequest r1 = builder.build(); Assertions.assertNotNull(r1); Assertions.assertEquals("GET", r1.getMethod()); Assertions.assertNull(r1.getScheme()); Assertions.assertNull(r1.getAuthority()); Assertions.assertNull(r1.getPath()); Assertions.assertEquals(URI.create("/"), r1.getUri()); Assertions.assertNull(r1.getVersion()); builder.setUri(URI.create("http://host:1234/blah?param=value")); builder.setVersion(HttpVersion.HTTP_1_1); Assertions.assertEquals("http", builder.getScheme()); Assertions.assertEquals(new URIAuthority("host", 1234), builder.getAuthority()); Assertions.assertEquals("/blah?param=value", builder.getPath()); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value"), builder.getUri()); Assertions.assertEquals(HttpVersion.HTTP_1_1, builder.getVersion()); final SimpleHttpRequest r2 = builder.build(); Assertions.assertEquals("GET", r2.getMethod()); Assertions.assertEquals("http", r2.getScheme()); Assertions.assertEquals(new URIAuthority("host", 1234), r2.getAuthority()); Assertions.assertEquals("/blah?param=value", r2.getPath()); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value"), r2.getUri()); Assertions.assertEquals(HttpVersion.HTTP_1_1, builder.getVersion()); builder.setCharset(StandardCharsets.US_ASCII); builder.addParameter("param1", "value1"); builder.addParameter("param2", null); builder.addParameters(new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null)); Assertions.assertEquals(builder.getParameters(), Arrays.asList( new BasicNameValuePair("param1", "value1"), new BasicNameValuePair("param2", null), new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null) )); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value"), builder.getUri()); final SimpleHttpRequest r3 = builder.build(); Assertions.assertEquals("GET", r3.getMethod()); Assertions.assertEquals("http", r3.getScheme()); Assertions.assertEquals(new URIAuthority("host", 1234), r3.getAuthority()); Assertions.assertEquals("/blah?param=value¶m1=value1¶m2¶m3=value3¶m4", r3.getPath()); Assertions.assertEquals(URI.create("http://host:1234/blah?param=value¶m1=value1¶m2¶m3=value3¶m4"), r3.getUri()); builder.addHeader("h1", "v1"); builder.addHeader("h1", "v2"); builder.addHeader("h2", "v2"); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(builder.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(builder.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(builder.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); final SimpleHttpRequest r4 = builder.build(); assertThat(r4.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); assertThat(r4.getHeaders("h1"), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"))); assertThat(r4.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1")); assertThat(r4.getLastHeader("h1"), HeaderMatcher.same("h1", "v2")); builder.removeHeader(new BasicHeader("h1", "v2")); assertThat(builder.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); final SimpleHttpRequest r5 = builder.build(); assertThat(r5.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1"))); assertThat(r5.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2"))); builder.removeHeaders("h1"); assertThat(builder.getHeaders("h1"), HeadersMatcher.same()); assertThat(builder.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); final SimpleHttpRequest r6 = builder.build(); assertThat(r6.getHeaders("h1"), HeadersMatcher.same()); assertThat(r6.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2"))); } @Test public void testResponseCopy() throws Exception { final SimpleHttpResponse response = SimpleHttpResponse.create(400); response.addHeader("h1", "v1"); response.addHeader("h1", "v2"); response.addHeader("h2", "v2"); response.setVersion(HttpVersion.HTTP_2); final SimpleResponseBuilder builder = SimpleResponseBuilder.copy(response); Assertions.assertEquals(400, builder.getStatus()); Assertions.assertEquals(HttpVersion.HTTP_2, builder.getVersion()); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); } @Test public void testRequestCopy() throws Exception { final SimpleHttpRequest request = SimpleHttpRequest.create(Method.GET, URI.create("https://host:3456/stuff?blah")) ; request.addHeader("h1", "v1"); request.addHeader("h1", "v2"); request.addHeader("h2", "v2"); request.setVersion(HttpVersion.HTTP_2); final SimpleRequestBuilder builder = SimpleRequestBuilder.copy(request); Assertions.assertEquals("GET", builder.getMethod()); Assertions.assertEquals("https", builder.getScheme()); Assertions.assertEquals(new URIAuthority("host", 3456), builder.getAuthority()); Assertions.assertEquals("/stuff?blah", builder.getPath()); Assertions.assertEquals(HttpVersion.HTTP_2, builder.getVersion()); assertThat(builder.getHeaders(), HeadersMatcher.same( new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2"))); } @Test public void testGetParameters() throws Exception { final SimpleRequestBuilder builder = SimpleRequestBuilder.get(URI.create("https://host:3456/stuff?p0=p0")); builder.addParameter("p1", "v1"); builder.addParameters(new BasicNameValuePair("p2", "v2"), new BasicNameValuePair("p3", "v3")); builder.addParameter(new BasicNameValuePair("p3", "v3.1")); Assertions.assertEquals("GET", builder.getMethod()); Assertions.assertEquals("https", builder.getScheme()); Assertions.assertEquals(new URIAuthority("host", 3456), builder.getAuthority()); Assertions.assertEquals("/stuff?p0=p0", builder.getPath()); assertThat(builder.getParameters(), NameValuePairsMatcher.same( new BasicNameValuePair("p1", "v1"), new BasicNameValuePair("p2", "v2"), new BasicNameValuePair("p3", "v3"), new BasicNameValuePair("p3", "v3.1"))); final SimpleHttpRequest request = builder.build(); assertThat(request.getPath(), CoreMatchers.equalTo("/stuff?p0=p0&p1=v1&p2=v2&p3=v3&p3=v3.1")); Assertions.assertNull(request.getBody()); } @Test public void testPostParameters() throws Exception { final SimpleRequestBuilder builder = SimpleRequestBuilder.post(URI.create("https://host:3456/stuff?p0=p0")); builder.addParameter("p1", "v1"); builder.addParameters(new BasicNameValuePair("p2", "v2"), new BasicNameValuePair("p3", "v3")); builder.addParameter(new BasicNameValuePair("p3", "v3.1")); Assertions.assertEquals("POST", builder.getMethod()); Assertions.assertEquals("https", builder.getScheme()); Assertions.assertEquals(new URIAuthority("host", 3456), builder.getAuthority()); Assertions.assertEquals("/stuff?p0=p0", builder.getPath()); assertThat(builder.getParameters(), NameValuePairsMatcher.same( new BasicNameValuePair("p1", "v1"), new BasicNameValuePair("p2", "v2"), new BasicNameValuePair("p3", "v3"), new BasicNameValuePair("p3", "v3.1"))); final SimpleHttpRequest request = builder.build(); assertThat(request.getPath(), CoreMatchers.equalTo("/stuff?p0=p0")); Assertions.assertNotNull(request.getBody()); assertThat(request.getBody().getContentType(), ContentTypeMatcher.sameMimeType(ContentType.APPLICATION_FORM_URLENCODED)); assertThat(request.getBody().getBodyText(), CoreMatchers.equalTo("p1=v1&p2=v2&p3=v3&p3=v3.1")); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/auth/000077500000000000000000000000001434266521000316505ustar00rootroot00000000000000TestAuthChallenge.java000066400000000000000000000047531434266521000360110ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.util.Arrays; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestAuthChallenge { @Test public void testAuthChallengeWithValue() { final AuthChallenge authChallenge = new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.BASIC, "blah", null); Assertions.assertEquals(StandardAuthScheme.BASIC, authChallenge.getSchemeName()); Assertions.assertEquals("blah", authChallenge.getValue()); Assertions.assertNull(authChallenge.getParams()); Assertions.assertEquals(StandardAuthScheme.BASIC + " blah", authChallenge.toString()); } @Test public void testAuthChallengeWithParams() { final AuthChallenge authChallenge = new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.BASIC, null, Arrays.asList(new BasicNameValuePair("blah", "this"), new BasicNameValuePair("blah", "that"))); Assertions.assertEquals(StandardAuthScheme.BASIC, authChallenge.getSchemeName()); Assertions.assertNull(authChallenge.getValue()); Assertions.assertNotNull(authChallenge.getParams()); Assertions.assertEquals(StandardAuthScheme.BASIC + " [blah=this, blah=that]", authChallenge.toString()); } } TestAuthScope.java000066400000000000000000000202071434266521000351700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link org.apache.hc.client5.http.auth.AuthScope}. */ public class TestAuthScope { @Test public void testBasics() { final AuthScope authscope = new AuthScope("http", "somehost", 80, "somerealm", "SomeScheme"); Assertions.assertEquals("SomeScheme", authscope.getSchemeName()); Assertions.assertEquals("http", authscope.getProtocol()); Assertions.assertEquals("somehost", authscope.getHost()); Assertions.assertEquals(80, authscope.getPort()); Assertions.assertEquals("somerealm", authscope.getRealm()); Assertions.assertEquals("SomeScheme 'somerealm' http://somehost:80", authscope.toString()); } @Test public void testByOrigin() { final HttpHost host = new HttpHost("http", "somehost", 8080); final AuthScope authscope = new AuthScope(host); Assertions.assertNull(authscope.getSchemeName()); Assertions.assertEquals("somehost", authscope.getHost()); Assertions.assertEquals(8080, authscope.getPort()); Assertions.assertNull(authscope.getRealm()); Assertions.assertEquals("http", authscope.getProtocol()); Assertions.assertEquals(" http://somehost:8080", authscope.toString()); } @Test public void testMixedCaseHostname() { final AuthScope authscope = new AuthScope("SomeHost", 80); Assertions.assertNull(authscope.getSchemeName()); Assertions.assertEquals("somehost", authscope.getHost()); Assertions.assertEquals(80, authscope.getPort()); Assertions.assertNull(authscope.getRealm()); Assertions.assertEquals(" ://somehost:80", authscope.toString()); } @Test public void testByOriginMixedCaseHostname() throws Exception { final HttpHost host = new HttpHost("http", "SomeHost", 8080); final AuthScope authscope = new AuthScope(host); Assertions.assertEquals("somehost", authscope.getHost()); } @Test public void testBasicsAllOptional() { final AuthScope authscope = new AuthScope(null, null, -1, null, null); Assertions.assertNull(authscope.getSchemeName()); Assertions.assertNull(authscope.getHost()); Assertions.assertEquals(-1, authscope.getPort()); Assertions.assertNull(authscope.getRealm()); Assertions.assertEquals(" ://:", authscope.toString()); } @Test public void testScopeMatching() { final AuthScope authscope1 = new AuthScope("http", "somehost", 80, "somerealm", "somescheme"); final AuthScope authscope2 = new AuthScope("http", "someotherhost", 80, "somerealm", "somescheme"); Assertions.assertTrue(authscope1.match(authscope2) < 0); int m1 = authscope1.match(new AuthScope(null, null, -1, null, "somescheme")); int m2 = authscope1.match(new AuthScope(null, null, -1, "somerealm", null)); Assertions.assertTrue(m2 > m1); m1 = authscope1.match(new AuthScope(null, null, -1, null, "somescheme")); m2 = authscope1.match(new AuthScope(null, null, -1, "somerealm", null)); Assertions.assertTrue(m2 > m1); m1 = authscope1.match(new AuthScope(null, null, -1, "somerealm", "somescheme")); m2 = authscope1.match(new AuthScope(null, null, 80, null, null)); Assertions.assertTrue(m2 > m1); m1 = authscope1.match(new AuthScope(null, null, 80, "somerealm", "somescheme")); m2 = authscope1.match(new AuthScope(null, "somehost", -1, null, null)); Assertions.assertTrue(m2 > m1); m1 = authscope1.match(new AuthScope(null, null, 80, "somerealm", "somescheme")); m2 = authscope1.match(new AuthScope(null, "somehost", -1, null, null)); Assertions.assertTrue(m2 > m1); m1 = authscope1.match(new AuthScope(null, null, -1, null, null)); m2 = authscope1.match(new AuthScope(null, null, -1, null, "somescheme")); Assertions.assertTrue(m2 > m1); } @Test public void testEquals() { final AuthScope authscope1 = new AuthScope("http", "somehost", 80, "somerealm", "somescheme"); final AuthScope authscope2 = new AuthScope("http", "someotherhost", 80, "somerealm", "somescheme"); final AuthScope authscope3 = new AuthScope("http", "somehost", 80, "somerealm", "somescheme"); final AuthScope authscope4 = new AuthScope("http", "somehost", 8080, "somerealm", "somescheme"); final AuthScope authscope5 = new AuthScope("http", "somehost", 80, "someotherrealm", "somescheme"); final AuthScope authscope6 = new AuthScope("http", "somehost", 80, "somerealm", "someotherscheme"); final AuthScope authscope7 = new AuthScope("https", "somehost", 80, "somerealm", "somescheme"); final AuthScope authscope8 = new AuthScope("https", "somehost", 80, "somerealm", "SomeScheme"); Assertions.assertEquals(authscope1, authscope1); Assertions.assertNotEquals(authscope1, authscope2); Assertions.assertEquals(authscope1, authscope3); Assertions.assertNotEquals(authscope1, authscope4); Assertions.assertNotEquals(authscope1, authscope5); Assertions.assertNotEquals(authscope1, authscope6); Assertions.assertNotEquals(authscope1, authscope7); Assertions.assertEquals(authscope7, authscope8); } @Test public void testHash() { final AuthScope authscope1 = new AuthScope("http", "somehost", 80, "somerealm", "somescheme"); final AuthScope authscope2 = new AuthScope("http", "someotherhost", 80, "somerealm", "somescheme"); final AuthScope authscope3 = new AuthScope("http", "somehost", 80, "somerealm", "somescheme"); final AuthScope authscope4 = new AuthScope("http", "somehost", 8080, "somerealm", "somescheme"); final AuthScope authscope5 = new AuthScope("http", "somehost", 80, "someotherrealm", "somescheme"); final AuthScope authscope6 = new AuthScope("http", "somehost", 80, "somerealm", "someotherscheme"); final AuthScope authscope7 = new AuthScope("https", "somehost", 80, "somerealm", "somescheme"); final AuthScope authscope8 = new AuthScope("https", "somehost", 80, "somerealm", "SomeScheme"); Assertions.assertEquals(authscope1.hashCode(), authscope1.hashCode()); Assertions.assertNotEquals(authscope1.hashCode(), authscope2.hashCode()); Assertions.assertEquals(authscope1.hashCode(), authscope3.hashCode()); Assertions.assertNotEquals(authscope1.hashCode(), authscope4.hashCode()); Assertions.assertNotEquals(authscope1.hashCode(), authscope5.hashCode()); Assertions.assertNotEquals(authscope1.hashCode(), authscope6.hashCode()); Assertions.assertNotEquals(authscope1.hashCode(), authscope7.hashCode()); Assertions.assertEquals(authscope7.hashCode(), authscope8.hashCode()); } } TestCredentials.java000066400000000000000000000227701434266521000355410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.auth; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestCredentials { @Test public void testUsernamePasswordCredentialsBasics() { final UsernamePasswordCredentials creds1 = new UsernamePasswordCredentials( "name","pwd".toCharArray()); Assertions.assertEquals("name", creds1.getUserName()); Assertions.assertEquals(new BasicUserPrincipal("name"), creds1.getUserPrincipal()); Assertions.assertArrayEquals("pwd".toCharArray(), creds1.getPassword()); Assertions.assertEquals("[principal: name]", creds1.toString()); final UsernamePasswordCredentials creds2 = new UsernamePasswordCredentials( "name", null); Assertions.assertEquals("name", creds2.getUserName()); Assertions.assertEquals(new BasicUserPrincipal("name"), creds2.getUserPrincipal()); Assertions.assertNull(creds2.getPassword()); Assertions.assertEquals("[principal: name]", creds2.toString()); } @Test public void testNTCredentialsBasics() { final NTCredentials creds1 = new NTCredentials( "name","pwd".toCharArray(), "localhost", "domain"); Assertions.assertEquals("name", creds1.getUserName()); Assertions.assertEquals(new NTUserPrincipal("DOMAIN", "name"), creds1.getUserPrincipal()); Assertions.assertArrayEquals("pwd".toCharArray(), creds1.getPassword()); Assertions.assertEquals("[principal: DOMAIN\\name][workstation: LOCALHOST][netbiosDomain: DOMAIN]", creds1.toString()); final NTCredentials creds2 = new NTCredentials( "name", null, null, null); Assertions.assertEquals("name", creds2.getUserName()); Assertions.assertEquals(new NTUserPrincipal(null, "name"), creds2.getUserPrincipal()); Assertions.assertNull(creds2.getPassword()); Assertions.assertEquals("[principal: name][workstation: null][netbiosDomain: null]", creds2.toString()); } @Test public void testUsernamePasswordCredentialsHashCode() { final UsernamePasswordCredentials creds1 = new UsernamePasswordCredentials( "name","pwd".toCharArray()); final UsernamePasswordCredentials creds2 = new UsernamePasswordCredentials( "othername","pwd".toCharArray()); final UsernamePasswordCredentials creds3 = new UsernamePasswordCredentials( "name", "otherpwd".toCharArray()); Assertions.assertTrue(creds1.hashCode() == creds1.hashCode()); Assertions.assertTrue(creds1.hashCode() != creds2.hashCode()); Assertions.assertTrue(creds1.hashCode() == creds3.hashCode()); } @Test public void testUsernamePasswordCredentialsEquals() { final UsernamePasswordCredentials creds1 = new UsernamePasswordCredentials( "name","pwd".toCharArray()); final UsernamePasswordCredentials creds2 = new UsernamePasswordCredentials( "othername","pwd".toCharArray()); final UsernamePasswordCredentials creds3 = new UsernamePasswordCredentials( "name", "otherpwd".toCharArray()); Assertions.assertEquals(creds1, creds1); Assertions.assertNotEquals(creds1, creds2); Assertions.assertEquals(creds1, creds3); } @Test public void testNTCredentialsHashCode() { final NTCredentials creds1 = new NTCredentials( "name","pwd".toCharArray(), "somehost", "domain"); final NTCredentials creds2 = new NTCredentials( "othername","pwd".toCharArray(), "somehost", "domain"); final NTCredentials creds3 = new NTCredentials( "name", "otherpwd".toCharArray(), "SomeHost", "Domain"); final NTCredentials creds4 = new NTCredentials( "name","pwd".toCharArray(), "otherhost", "domain"); final NTCredentials creds5 = new NTCredentials( "name","pwd".toCharArray(), null, "domain"); final NTCredentials creds6 = new NTCredentials( "name","pwd".toCharArray(), "somehost", "ms"); final NTCredentials creds7 = new NTCredentials( "name","pwd".toCharArray(), "somehost", null); final NTCredentials creds8 = new NTCredentials( "name","pwd".toCharArray(), null, "domain"); final NTCredentials creds9 = new NTCredentials( "name","pwd".toCharArray(), "somehost", null); Assertions.assertTrue(creds1.hashCode() == creds1.hashCode()); Assertions.assertTrue(creds1.hashCode() != creds2.hashCode()); Assertions.assertEquals(creds1.hashCode(), creds3.hashCode()); Assertions.assertNotEquals(creds1.hashCode(), creds4.hashCode()); Assertions.assertNotEquals(creds1.hashCode(), creds5.hashCode()); Assertions.assertNotEquals(creds1.hashCode(), creds6.hashCode()); Assertions.assertNotEquals(creds1.hashCode(), creds7.hashCode()); Assertions.assertEquals(creds8.hashCode(), creds5.hashCode()); Assertions.assertEquals(creds9.hashCode(), creds7.hashCode()); } @Test public void testNTCredentialsEquals() { final NTCredentials creds1 = new NTCredentials( "name","pwd".toCharArray(), "somehost", "domain"); final NTCredentials creds2 = new NTCredentials( "othername","pwd".toCharArray(), "somehost", "domain"); final NTCredentials creds3 = new NTCredentials( "name", "otherpwd".toCharArray(), "SomeHost", "Domain"); final NTCredentials creds4 = new NTCredentials( "name","pwd".toCharArray(), "otherhost", "domain"); final NTCredentials creds5 = new NTCredentials( "name","pwd".toCharArray(), null, "domain"); final NTCredentials creds6 = new NTCredentials( "name","pwd".toCharArray(), "somehost", "ms"); final NTCredentials creds7 = new NTCredentials( "name","pwd".toCharArray(), "somehost", null); final NTCredentials creds8 = new NTCredentials( "name","pwd".toCharArray(), null, "domain"); final NTCredentials creds9 = new NTCredentials( "name","pwd".toCharArray(), "somehost", null); Assertions.assertEquals(creds1, creds1); Assertions.assertNotEquals(creds1, creds2); Assertions.assertEquals(creds1, creds3); Assertions.assertNotEquals(creds1, creds4); Assertions.assertNotEquals(creds1, creds5); Assertions.assertNotEquals(creds1, creds6); Assertions.assertNotEquals(creds1, creds7); Assertions.assertEquals(creds8, creds5); Assertions.assertEquals(creds9, creds7); } @Test public void testUsernamePasswordCredentialsSerialization() throws Exception { final UsernamePasswordCredentials orig = new UsernamePasswordCredentials("name","pwd".toCharArray()); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final UsernamePasswordCredentials clone = (UsernamePasswordCredentials) inStream.readObject(); Assertions.assertEquals(orig, clone); } @Test public void testNTCredentialsSerialization() throws Exception { final NTCredentials orig = new NTCredentials("name","pwd".toCharArray(), "somehost", "domain"); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final NTCredentials clone = (NTCredentials) inStream.readObject(); Assertions.assertEquals(orig, clone); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/classic/000077500000000000000000000000001434266521000323305ustar00rootroot00000000000000methods/000077500000000000000000000000001434266521000337145ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/classicTestHttpOptions.java000066400000000000000000000035501434266521000377150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import java.util.Set; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHttpOptions { @Test public void testMultipleAllows() { final BasicHttpResponse resp = new BasicHttpResponse(200, "test reason"); resp.addHeader("Allow", "POST"); resp.addHeader("Allow", "GET"); final HttpOptions opt = new HttpOptions("*"); final Set methodsName = opt.getAllowedMethods(resp); Assertions.assertTrue(methodsName.contains("POST")); Assertions.assertTrue(methodsName.contains("GET")); } } TestHttpRequestBase.java000066400000000000000000000155201434266521000405050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URI; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHttpRequestBase { private static final String HOT_URL = "http://host/path"; @Test public void testBasicGetMethodProperties() throws Exception { final HttpGet httpget = new HttpGet(HOT_URL); Assertions.assertEquals("GET", httpget.getMethod()); Assertions.assertEquals(new URI(HOT_URL), httpget.getUri()); } @Test public void testBasicHttpPostMethodProperties() throws Exception { final HttpPost HttpPost = new HttpPost(HOT_URL); Assertions.assertEquals("POST", HttpPost.getMethod()); Assertions.assertEquals(new URI(HOT_URL), HttpPost.getUri()); } @Test public void testBasicHttpHeadMethodProperties() throws Exception { final HttpHead httpHead = new HttpHead(HOT_URL); Assertions.assertEquals("HEAD", httpHead.getMethod()); Assertions.assertEquals(new URI(HOT_URL), httpHead.getUri()); } @Test public void testBasicHttpOptionMethodProperties() throws Exception { final HttpOptions httpOption = new HttpOptions(HOT_URL); Assertions.assertEquals("OPTIONS", httpOption.getMethod()); Assertions.assertEquals(new URI(HOT_URL), httpOption.getUri()); } @Test public void testBasicHttpPatchMethodProperties() throws Exception { final HttpPatch httpPatch = new HttpPatch(HOT_URL); Assertions.assertEquals("PATCH", httpPatch.getMethod()); Assertions.assertEquals(new URI(HOT_URL), httpPatch.getUri()); } @Test public void testBasicHttpPutMethodProperties() throws Exception { final HttpPut httpPut = new HttpPut(HOT_URL); Assertions.assertEquals("PUT", httpPut.getMethod()); Assertions.assertEquals(new URI(HOT_URL), httpPut.getUri()); } @Test public void testBasicHttpTraceMethodProperties() throws Exception { final HttpTrace httpTrace = new HttpTrace(HOT_URL); Assertions.assertEquals("TRACE", httpTrace.getMethod()); Assertions.assertEquals(new URI(HOT_URL), httpTrace.getUri()); } @Test public void testBasicHttpDeleteMethodProperties() throws Exception { final HttpDelete httpDelete = new HttpDelete(HOT_URL); Assertions.assertEquals("DELETE", httpDelete.getMethod()); Assertions.assertEquals(new URI(HOT_URL), httpDelete.getUri()); } @Test public void testGetMethodEmptyURI() throws Exception { final HttpGet httpget = new HttpGet(""); Assertions.assertEquals(new URI("/"), httpget.getUri()); } @Test public void testPostMethodEmptyURI() throws Exception { final HttpPost HttpPost = new HttpPost(""); Assertions.assertEquals(new URI("/"), HttpPost.getUri()); } @Test public void testHeadMethodEmptyURI() throws Exception { final HttpHead httpHead = new HttpHead(""); Assertions.assertEquals(new URI("/"), httpHead.getUri()); } @Test public void testOptionMethodEmptyURI() throws Exception { final HttpOptions httpOption = new HttpOptions(""); Assertions.assertEquals(new URI("/"), httpOption.getUri()); } @Test public void testPatchMethodEmptyURI() throws Exception { final HttpPatch httpPatch = new HttpPatch(""); Assertions.assertEquals(new URI("/"), httpPatch.getUri()); } @Test public void testPutMethodEmptyURI() throws Exception { final HttpPut httpPut = new HttpPut(""); Assertions.assertEquals(new URI("/"), httpPut.getUri()); } @Test public void testTraceMethodEmptyURI() throws Exception { final HttpTrace httpTrace = new HttpTrace(""); Assertions.assertEquals(new URI("/"), httpTrace.getUri()); } @Test public void testDeleteMethodEmptyURI() throws Exception { final HttpDelete httpDelete = new HttpDelete(""); Assertions.assertEquals(new URI("/"), httpDelete.getUri()); } @Test public void testTraceMethodSetEntity() { final HttpTrace httpTrace = new HttpTrace(HOT_URL); final HttpEntity entity = EntityBuilder.create().setText("stuff").build(); assertThrows(IllegalStateException.class, () -> httpTrace.setEntity(entity)); } @Test public void testOptionMethodGetAllowedMethods() { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.addHeader("Allow", "GET, HEAD"); response.addHeader("Allow", "DELETE"); response.addHeader("Content-Length", "128"); final HttpOptions httpOptions = new HttpOptions(""); final Set methods = httpOptions.getAllowedMethods(response); assertAll("Must all pass", () -> assertFalse(methods.isEmpty()), () -> assertEquals(methods.size(), 3), () -> assertTrue(methods.containsAll(Stream.of("HEAD", "DELETE", "GET") .collect(Collectors.toCollection(HashSet::new)))) ); } } TestHttpTrace.java000066400000000000000000000030461434266521000373200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/classic/methods/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.classic.methods; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestHttpTrace { @Test public void testHttpTraceSetEntity() { final HttpTrace httpTrace = new HttpTrace("/path"); Assertions.assertThrows(IllegalStateException.class, () -> httpTrace.setEntity(null)); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/config/000077500000000000000000000000001434266521000321545ustar00rootroot00000000000000TestRequestConfig.java000066400000000000000000000102001434266521000363470ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/config/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.config; import java.util.Collections; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRequestConfig { @Test public void testBasics() { final RequestConfig config = RequestConfig.custom().build(); config.toString(); } @Test public void testDefaults() { final RequestConfig config = RequestConfig.DEFAULT; Assertions.assertEquals(Timeout.ofMinutes(3), config.getConnectionRequestTimeout()); Assertions.assertFalse(config.isExpectContinueEnabled()); Assertions.assertTrue(config.isAuthenticationEnabled()); Assertions.assertTrue(config.isRedirectsEnabled()); Assertions.assertFalse(config.isCircularRedirectsAllowed()); Assertions.assertEquals(50, config.getMaxRedirects()); Assertions.assertNull(config.getCookieSpec()); Assertions.assertNull(config.getTargetPreferredAuthSchemes()); Assertions.assertNull(config.getProxyPreferredAuthSchemes()); Assertions.assertTrue(config.isContentCompressionEnabled()); } @Test public void testBuildAndCopy() throws Exception { final RequestConfig config0 = RequestConfig.custom() .setConnectionRequestTimeout(44, TimeUnit.MILLISECONDS) .setExpectContinueEnabled(true) .setAuthenticationEnabled(false) .setRedirectsEnabled(false) .setCircularRedirectsAllowed(true) .setMaxRedirects(100) .setCookieSpec(StandardCookieSpec.STRICT) .setTargetPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.NTLM)) .setProxyPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.DIGEST)) .setContentCompressionEnabled(false) .build(); final RequestConfig config = RequestConfig.copy(config0).build(); Assertions.assertEquals(TimeValue.ofMilliseconds(44), config.getConnectionRequestTimeout()); Assertions.assertTrue(config.isExpectContinueEnabled()); Assertions.assertFalse(config.isAuthenticationEnabled()); Assertions.assertFalse(config.isRedirectsEnabled()); Assertions.assertTrue(config.isCircularRedirectsAllowed()); Assertions.assertEquals(100, config.getMaxRedirects()); Assertions.assertEquals(StandardCookieSpec.STRICT, config.getCookieSpec()); Assertions.assertEquals(Collections.singletonList(StandardAuthScheme.NTLM), config.getTargetPreferredAuthSchemes()); Assertions.assertEquals(Collections.singletonList(StandardAuthScheme.DIGEST), config.getProxyPreferredAuthSchemes()); Assertions.assertFalse(config.isContentCompressionEnabled()); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/cookie/000077500000000000000000000000001434266521000321605ustar00rootroot00000000000000TestCookieOrigin.java000066400000000000000000000054531434266521000361740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Test cases for {@link CookieOrigin}. */ public class TestCookieOrigin { @Test public void testConstructor() { final CookieOrigin origin = new CookieOrigin("www.apache.org", 80, "/", false); Assertions.assertEquals("www.apache.org", origin.getHost()); Assertions.assertEquals(80, origin.getPort()); Assertions.assertEquals("/", origin.getPath()); Assertions.assertFalse(origin.isSecure()); } @Test public void testNullHost() { Assertions.assertThrows(NullPointerException.class, () -> new CookieOrigin(null, 80, "/", false)); } @Test public void testEmptyHost() { Assertions.assertThrows(IllegalArgumentException.class, () -> new CookieOrigin(" ", 80, "/", false)); } @Test public void testNegativePort() { Assertions.assertThrows(IllegalArgumentException.class, () -> new CookieOrigin("www.apache.org", -80, "/", false)); } @Test public void testNullPath() { Assertions.assertThrows(NullPointerException.class, () -> new CookieOrigin("www.apache.org", 80, null, false)); } @Test public void testEmptyPath() { final CookieOrigin origin = new CookieOrigin("www.apache.org", 80, "", false); Assertions.assertEquals("www.apache.org", origin.getHost()); Assertions.assertEquals(80, origin.getPort()); Assertions.assertEquals("/", origin.getPath()); Assertions.assertFalse(origin.isSecure()); } } TestCookiePathComparator.java000066400000000000000000000106471434266521000376720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.util.Comparator; import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Test cases for {@link CookiePathComparator}. */ public class TestCookiePathComparator { @Test public void testUnequality1() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/a/b/"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/a/"); final Comparator comparator = new CookiePathComparator(); Assertions.assertTrue(comparator.compare(cookie1, cookie2) < 0); Assertions.assertTrue(comparator.compare(cookie2, cookie1) > 0); } @Test public void testUnequality2() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/a/b"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/a"); final Comparator comparator = new CookiePathComparator(); Assertions.assertTrue(comparator.compare(cookie1, cookie2) < 0); Assertions.assertTrue(comparator.compare(cookie2, cookie1) > 0); } @Test public void testEquality1() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/a"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/a"); final Comparator comparator = new CookiePathComparator(); Assertions.assertEquals(0, comparator.compare(cookie1, cookie2)); Assertions.assertEquals(0, comparator.compare(cookie2, cookie1)); } @Test public void testEquality2() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/a/"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/a"); final Comparator comparator = new CookiePathComparator(); Assertions.assertEquals(0, comparator.compare(cookie1, cookie2)); Assertions.assertEquals(0, comparator.compare(cookie2, cookie1)); } @Test public void testEquality3() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath(null); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/"); final Comparator comparator = new CookiePathComparator(); Assertions.assertEquals(0, comparator.compare(cookie1, cookie2)); Assertions.assertEquals(0, comparator.compare(cookie2, cookie1)); } @Test public void testEquality4() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/this"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/that"); final Comparator comparator = new CookiePathComparator(); Assertions.assertEquals(0, comparator.compare(cookie1, cookie2)); Assertions.assertEquals(0, comparator.compare(cookie2, cookie1)); } } TestCookiePriorityComparator.java000066400000000000000000000106121434266521000406070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.cookie; import java.time.Instant; import java.util.Comparator; import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Test cases for {@link org.apache.hc.client5.http.cookie.CookiePriorityComparator}. */ public class TestCookiePriorityComparator { private Comparator comparator; @BeforeEach public void setup() { comparator = CookiePriorityComparator.INSTANCE; } @Test public void testUnequality() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/a/b/"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/a/"); Assertions.assertTrue(comparator.compare(cookie1, cookie2) < 0); Assertions.assertTrue(comparator.compare(cookie2, cookie1) > 0); } @Test public void testEquality() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/a"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/a"); Assertions.assertEquals(0, comparator.compare(cookie1, cookie2)); Assertions.assertEquals(0, comparator.compare(cookie2, cookie1)); } @Test public void testUnequalityTrailingSlash() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/a/"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/a"); Assertions.assertTrue(comparator.compare(cookie1, cookie2) < 0); Assertions.assertTrue(comparator.compare(cookie2, cookie1) > 0); } @Test public void testEqualityNullPath() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath(null); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/"); Assertions.assertEquals(0, comparator.compare(cookie1, cookie2)); Assertions.assertEquals(0, comparator.compare(cookie2, cookie1)); } @Test public void testEqualitySameLength() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/this"); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/that"); Assertions.assertEquals(0, comparator.compare(cookie1, cookie2)); Assertions.assertEquals(0, comparator.compare(cookie2, cookie1)); } @Test public void testUnequalityCreationDate() { final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value"); cookie1.setPath("/blah"); cookie1.setCreationDate(Instant.now().minusMillis(200000)); final BasicClientCookie cookie2 = new BasicClientCookie("name1", "value"); cookie2.setPath("/blah"); cookie2.setCreationDate(Instant.now()); Assertions.assertTrue(comparator.compare(cookie1, cookie2) < 0); Assertions.assertTrue(comparator.compare(cookie2, cookie1) > 0); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/000077500000000000000000000000001434266521000322235ustar00rootroot00000000000000TestBrotli.java000066400000000000000000000037341434266521000351110ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestBrotli { /** * Brotli decompression test implemented by request with specified response encoding br * * @throws Exception */ @Test public void testDecompressionWithBrotli() throws Exception { final byte[] bytes = new byte[] {33, 44, 0, 4, 116, 101, 115, 116, 32, 98, 114, 111, 116, 108, 105, 10, 3}; final HttpEntity entity = new BrotliDecompressingEntity(new ByteArrayEntity(bytes, null)); Assertions.assertEquals("test brotli\n", EntityUtils.toString(entity)); } } TestDecompressingEntity.java000066400000000000000000000102611434266521000376460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; import java.util.zip.Checksum; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDecompressingEntity { @Test public void testNonStreaming() throws Exception { final CRC32 crc32 = new CRC32(); final StringEntity wrapped = new StringEntity("1234567890", StandardCharsets.US_ASCII); final ChecksumEntity entity = new ChecksumEntity(wrapped, crc32); Assertions.assertFalse(entity.isStreaming()); final String s = EntityUtils.toString(entity); Assertions.assertEquals("1234567890", s); Assertions.assertEquals(639479525L, crc32.getValue()); final InputStream in1 = entity.getContent(); final InputStream in2 = entity.getContent(); Assertions.assertNotSame(in1, in2); } @Test public void testStreaming() throws Exception { final CRC32 crc32 = new CRC32(); final ByteArrayInputStream in = new ByteArrayInputStream("1234567890".getBytes(StandardCharsets.US_ASCII)); final InputStreamEntity wrapped = new InputStreamEntity(in, -1, ContentType.DEFAULT_TEXT); final ChecksumEntity entity = new ChecksumEntity(wrapped, crc32); Assertions.assertTrue(entity.isStreaming()); final String s = EntityUtils.toString(entity); Assertions.assertEquals("1234567890", s); Assertions.assertEquals(639479525L, crc32.getValue()); final InputStream in1 = entity.getContent(); final InputStream in2 = entity.getContent(); Assertions.assertSame(in1, in2); EntityUtils.consume(entity); EntityUtils.consume(entity); } @Test public void testWriteToStream() throws Exception { final CRC32 crc32 = new CRC32(); final StringEntity wrapped = new StringEntity("1234567890", StandardCharsets.US_ASCII); try (final ChecksumEntity entity = new ChecksumEntity(wrapped, crc32)) { Assertions.assertFalse(entity.isStreaming()); final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); final String s = new String(out.toByteArray(), StandardCharsets.US_ASCII); Assertions.assertEquals("1234567890", s); Assertions.assertEquals(639479525L, crc32.getValue()); } } static class ChecksumEntity extends DecompressingEntity { public ChecksumEntity(final HttpEntity wrapped, final Checksum checksum) { super(wrapped, inStream -> new CheckedInputStream(inStream, checksum)); } } } TestDeflate.java000066400000000000000000000043531434266521000352200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.nio.charset.StandardCharsets; import java.util.zip.Deflater; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDeflate { @Test public void testCompressDecompress() throws Exception { final String s = "some kind of text"; final byte[] input = s.getBytes(StandardCharsets.US_ASCII); // Compress the bytes final byte[] compressed = new byte[input.length * 2]; final Deflater compresser = new Deflater(); compresser.setInput(input); compresser.finish(); final int len = compresser.deflate(compressed); final HttpEntity entity = new DeflateDecompressingEntity(new ByteArrayEntity(compressed, 0, len, ContentType.APPLICATION_OCTET_STREAM)); Assertions.assertEquals(s, EntityUtils.toString(entity)); } } TestEntityBuilder.java000066400000000000000000000124341434266521000364360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.File; import java.io.InputStream; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class TestEntityBuilder { @Test public void testBuildEmptyEntity() throws Exception { Assertions.assertThrows(IllegalStateException.class, () -> EntityBuilder.create().build()); } @Test public void testBuildTextEntity() throws Exception { final HttpEntity entity = EntityBuilder.create().setText("stuff").build(); Assertions.assertNotNull(entity); Assertions.assertNotNull(entity.getContent()); Assertions.assertNotNull(entity.getContentType()); Assertions.assertEquals("text/plain; charset=ISO-8859-1", entity.getContentType()); } @Test public void testBuildBinaryEntity() throws Exception { final HttpEntity entity = EntityBuilder.create().setBinary(new byte[] {0, 1, 2}).build(); Assertions.assertNotNull(entity); Assertions.assertNotNull(entity.getContent()); Assertions.assertNotNull(entity.getContentType()); Assertions.assertEquals("application/octet-stream", entity.getContentType()); } @Test public void testBuildStreamEntity() throws Exception { final InputStream in = Mockito.mock(InputStream.class); final HttpEntity entity = EntityBuilder.create().setStream(in).build(); Assertions.assertNotNull(entity); Assertions.assertNotNull(entity.getContent()); Assertions.assertNotNull(entity.getContentType()); Assertions.assertEquals(-1, entity.getContentLength()); Assertions.assertEquals("application/octet-stream", entity.getContentType()); } @Test public void testBuildSerializableEntity() throws Exception { final HttpEntity entity = EntityBuilder.create().setSerializable(Boolean.TRUE).build(); Assertions.assertNotNull(entity); Assertions.assertNotNull(entity.getContent()); Assertions.assertNotNull(entity.getContentType()); Assertions.assertEquals("application/octet-stream", entity.getContentType()); } @Test public void testBuildFileEntity() throws Exception { final File file = new File("stuff"); final HttpEntity entity = EntityBuilder.create().setFile(file).build(); Assertions.assertNotNull(entity); Assertions.assertNotNull(entity.getContentType()); Assertions.assertEquals("application/octet-stream", entity.getContentType()); } @Test public void testExplicitContentProperties() throws Exception { final HttpEntity entity = EntityBuilder.create() .setContentType(ContentType.APPLICATION_JSON) .setContentEncoding("identity") .setBinary(new byte[] {0, 1, 2}) .setText("{\"stuff\"}").build(); Assertions.assertNotNull(entity); Assertions.assertNotNull(entity.getContentType()); Assertions.assertEquals("application/json; charset=UTF-8", entity.getContentType()); Assertions.assertNotNull(entity.getContentEncoding()); Assertions.assertEquals("identity", entity.getContentEncoding()); Assertions.assertEquals("{\"stuff\"}", EntityUtils.toString(entity)); } @Test public void testBuildChunked() throws Exception { final HttpEntity entity = EntityBuilder.create().setText("stuff").chunked().build(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity.isChunked()); } @Test public void testBuildGZipped() throws Exception { final HttpEntity entity = EntityBuilder.create().setText("stuff").gzipCompressed().build(); Assertions.assertNotNull(entity); Assertions.assertNotNull(entity.getContentType()); Assertions.assertEquals("text/plain; charset=ISO-8859-1", entity.getContentType()); Assertions.assertNotNull(entity.getContentEncoding()); Assertions.assertEquals("gzip", entity.getContentEncoding()); } } TestGZip.java000066400000000000000000000113461434266521000345250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestGZip { @Test public void testBasic() throws Exception { final String s = "some kind of text"; final StringEntity e = new StringEntity(s, ContentType.TEXT_PLAIN, false); try (final GzipCompressingEntity gzipe = new GzipCompressingEntity(e)) { Assertions.assertTrue(gzipe.isChunked()); Assertions.assertEquals(-1, gzipe.getContentLength()); Assertions.assertNotNull(gzipe.getContentEncoding()); Assertions.assertEquals("gzip", gzipe.getContentEncoding()); } } @Test public void testCompressionDecompression() throws Exception { final StringEntity in = new StringEntity("some kind of text", ContentType.TEXT_PLAIN); try (final GzipCompressingEntity gzipe = new GzipCompressingEntity(in)) { final ByteArrayOutputStream buf = new ByteArrayOutputStream(); gzipe.writeTo(buf); final ByteArrayEntity out = new ByteArrayEntity(buf.toByteArray(), ContentType.APPLICATION_OCTET_STREAM); final GzipDecompressingEntity gunzipe = new GzipDecompressingEntity(out); Assertions.assertEquals("some kind of text", EntityUtils.toString(gunzipe, StandardCharsets.US_ASCII)); } } @Test public void testCompressionIOExceptionLeavesOutputStreamOpen() throws Exception { final HttpEntity in = Mockito.mock(HttpEntity.class); Mockito.doThrow(new IOException("Ooopsie")).when(in).writeTo(ArgumentMatchers.any()); try (final GzipCompressingEntity gzipe = new GzipCompressingEntity(in)) { final OutputStream out = Mockito.mock(OutputStream.class); try { gzipe.writeTo(out); } catch (final IOException ex) { Mockito.verify(out, Mockito.never()).close(); } } } @Test public void testDecompressionWithMultipleGZipStream() throws Exception { final int[] data = new int[] { 0x1f, 0x8b, 0x08, 0x08, 0x03, 0xf1, 0x55, 0x5a, 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x31, 0x00, 0x2b, 0x2e, 0x29, 0x4a, 0x4d, 0xcc, 0xd5, 0x35, 0xe4, 0x02, 0x00, 0x03, 0x61, 0xf0, 0x5f, 0x09, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x08, 0x08, 0xf1, 0x55, 0x5a, 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x32, 0x00, 0x2b, 0x2e, 0x29, 0x4a, 0x4d, 0xcc, 0xd5, 0x35, 0xe2, 0x02, 0x00, 0xc0, 0x32, 0xdd, 0x74, 0x09, 0x00, 0x00, 0x00 }; final byte[] bytes = new byte[data.length]; for (int i = 0; i < data.length; i++) { bytes[i] = (byte) (data[i] & 0xff); } try (final GzipDecompressingEntity entity = new GzipDecompressingEntity(new InputStreamEntity(new ByteArrayInputStream(bytes), ContentType.APPLICATION_OCTET_STREAM))) { Assertions.assertEquals("stream-1\nstream-2\n", EntityUtils.toString(entity, StandardCharsets.US_ASCII)); } } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/000077500000000000000000000000001434266521000331525ustar00rootroot00000000000000FormBodyPartTest.java000066400000000000000000000033021434266521000371440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.File; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class FormBodyPartTest { @Test public void testConstructorCompat() throws Exception { final File tmp= File.createTempFile("test", "test"); tmp.deleteOnExit(); final FileBody obj = new FileBody(tmp, ContentType.APPLICATION_OCTET_STREAM); Assertions.assertEquals(tmp.getName(), obj.getFilename()); } } HttpRFC7578MultipartTest.java000066400000000000000000000045331434266521000403120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class HttpRFC7578MultipartTest { @Test public void testPercentDecodingWithTooShortMessage() throws Exception { Assertions.assertThrows(java.lang.IllegalArgumentException.class, () -> new HttpRFC7578Multipart.PercentCodec().decode("%".getBytes())); } @Test public void testPercentDecodingWithValidMessages() throws Exception { final HttpRFC7578Multipart.PercentCodec codec = new HttpRFC7578Multipart.PercentCodec(); final String[][] tests = new String[][] { {"test", "test"}, {"%20", " "}, {"a%20b", "a b"}, {"https%3A%2F%2Fhc.apache.org%2Fhttpcomponents-client-5.0.x%2Findex.html", "https://hc.apache.org/httpcomponents-client-5.0.x/index.html"}, {"%00", "\00"}, {"%0A", "\n"}, }; for (final String[] test : tests) { assertEquals(test[1], new String(codec.decode(test[0].getBytes()))); } } }TestFormBodyPartBuilder.java000066400000000000000000000164201434266521000404600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.File; import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestFormBodyPartBuilder { @Test public void testBuildBodyPartBasics() throws Exception { final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN); final FormBodyPart bodyPart = FormBodyPartBuilder.create() .setName("blah") .setBody(stringBody) .build(); Assertions.assertNotNull(bodyPart); Assertions.assertEquals("blah", bodyPart.getName()); Assertions.assertEquals(stringBody, bodyPart.getBody()); final Header header = bodyPart.getHeader(); Assertions.assertNotNull(header); assertFields(Arrays.asList( new MimeField("Content-Disposition", "form-data; name=\"blah\""), new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header.getFields()); } @Test public void testBuildBodyPartMultipleBuilds() throws Exception { final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN); final FormBodyPartBuilder builder = FormBodyPartBuilder.create(); final FormBodyPart bodyPart1 = builder .setName("blah") .setBody(stringBody) .build(); Assertions.assertNotNull(bodyPart1); Assertions.assertEquals("blah", bodyPart1.getName()); Assertions.assertEquals(stringBody, bodyPart1.getBody()); final Header header1 = bodyPart1.getHeader(); Assertions.assertNotNull(header1); assertFields(Arrays.asList( new MimeField("Content-Disposition", "form-data; name=\"blah\""), new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header1.getFields()); final FileBody fileBody = new FileBody(new File("/path/stuff.bin"), ContentType.DEFAULT_BINARY); final FormBodyPart bodyPart2 = builder .setName("yada") .setBody(fileBody) .build(); Assertions.assertNotNull(bodyPart2); Assertions.assertEquals("yada", bodyPart2.getName()); Assertions.assertEquals(fileBody, bodyPart2.getBody()); final Header header2 = bodyPart2.getHeader(); Assertions.assertNotNull(header2); assertFields(Arrays.asList( new MimeField("Content-Disposition", "form-data; name=\"yada\"; filename=\"stuff.bin\""), new MimeField("Content-Type", "application/octet-stream")), header2.getFields()); } @Test public void testBuildBodyPartCustomHeaders() throws Exception { final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN); final FormBodyPartBuilder builder = FormBodyPartBuilder.create("blah", stringBody); final FormBodyPart bodyPart1 = builder .addField("header1", "blah") .addField("header3", "blah") .addField("header3", "blah") .addField("header3", "blah") .addField("header3", "blah") .addField("header3", "blah") .build(); Assertions.assertNotNull(bodyPart1); final Header header1 = bodyPart1.getHeader(); Assertions.assertNotNull(header1); assertFields(Arrays.asList( new MimeField("header1", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("Content-Disposition", "form-data; name=\"blah\""), new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header1.getFields()); final FormBodyPart bodyPart2 = builder .setField("header2", "yada") .removeFields("header3") .build(); Assertions.assertNotNull(bodyPart2); final Header header2 = bodyPart2.getHeader(); Assertions.assertNotNull(header2); assertFields(Arrays.asList( new MimeField("header1", "blah"), new MimeField("header2", "yada"), new MimeField("Content-Disposition", "form-data; name=\"blah\""), new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header2.getFields()); final FormBodyPart bodyPart3 = builder .addField("Content-Disposition", "disposition stuff") .addField("Content-Type", "type stuff") .addField("Content-Transfer-Encoding", "encoding stuff") .build(); Assertions.assertNotNull(bodyPart3); final Header header3 = bodyPart3.getHeader(); Assertions.assertNotNull(header3); assertFields(Arrays.asList( new MimeField("header1", "blah"), new MimeField("header2", "yada"), new MimeField("Content-Disposition", "disposition stuff"), new MimeField("Content-Type", "type stuff"), new MimeField("Content-Transfer-Encoding", "encoding stuff")), header3.getFields()); } private static void assertFields(final List expected, final List result) { Assertions.assertNotNull(result); Assertions.assertEquals(expected.size(), result.size()); for (int i = 0; i < expected.size(); i++) { final MimeField expectedField = expected.get(i); final MimeField resultField = result.get(i); Assertions.assertNotNull(resultField); Assertions.assertEquals(expectedField.getName(), resultField.getName()); Assertions.assertEquals(expectedField.getBody(), resultField.getBody()); } } } TestMimeField.java000066400000000000000000000040551434266521000364350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.util.Arrays; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMimeField { @Test public void testBasics() { final MimeField f1 = new MimeField("some-field", "some-value", Arrays.asList( new BasicNameValuePair("p1", "this"), new BasicNameValuePair("p2", "that"), new BasicNameValuePair("p3", "\"this \\and\\ that\""))); Assertions.assertEquals("some-field", f1.getName()); Assertions.assertEquals("some-value", f1.getValue()); Assertions.assertEquals("some-value; p1=\"this\"; p2=\"that\"; p3=\"\\\"this \\\\and\\\\ that\\\"\"", f1.getBody()); } }TestMultipartContentBody.java000066400000000000000000000070611434266521000407340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMultipartContentBody { @Test public void testStringBody() throws Exception { final StringBody b1 = new StringBody("text", ContentType.DEFAULT_TEXT); Assertions.assertEquals(4, b1.getContentLength()); Assertions.assertEquals("ISO-8859-1", b1.getCharset()); Assertions.assertNull(b1.getFilename()); Assertions.assertEquals("text/plain", b1.getMimeType()); Assertions.assertEquals("text", b1.getMediaType()); Assertions.assertEquals("plain", b1.getSubType()); final StringBody b2 = new StringBody("more text", ContentType.create("text/other", StandardCharsets.ISO_8859_1)); Assertions.assertEquals(9, b2.getContentLength()); Assertions.assertEquals(StandardCharsets.ISO_8859_1.name(), b2.getCharset()); Assertions.assertNull(b2.getFilename()); Assertions.assertEquals("text/other", b2.getMimeType()); Assertions.assertEquals("text", b2.getMediaType()); Assertions.assertEquals("other", b2.getSubType()); } @Test public void testInputStreamBody() throws Exception { final byte[] stuff = "Stuff".getBytes(StandardCharsets.US_ASCII); final InputStreamBody b1 = new InputStreamBody(new ByteArrayInputStream(stuff), "stuff"); Assertions.assertEquals(-1, b1.getContentLength()); Assertions.assertNull(b1.getCharset()); Assertions.assertEquals("stuff", b1.getFilename()); Assertions.assertEquals("application/octet-stream", b1.getMimeType()); Assertions.assertEquals("application", b1.getMediaType()); Assertions.assertEquals("octet-stream", b1.getSubType()); final InputStreamBody b2 = new InputStreamBody( new ByteArrayInputStream(stuff), ContentType.create("some/stuff"), "stuff"); Assertions.assertEquals(-1, b2.getContentLength()); Assertions.assertNull(b2.getCharset()); Assertions.assertEquals("stuff", b2.getFilename()); Assertions.assertEquals("some/stuff", b2.getMimeType()); Assertions.assertEquals("some", b2.getMediaType()); Assertions.assertEquals("stuff", b2.getSubType()); } } TestMultipartEntityBuilder.java000066400000000000000000000251061434266521000412670ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMultipartEntityBuilder { @Test public void testBasics() throws Exception { final MultipartFormEntity entity = MultipartEntityBuilder.create().buildEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity.getMultipart() instanceof HttpStrictMultipart); Assertions.assertEquals(0, entity.getMultipart().getParts().size()); } @Test public void testMultipartOptions() throws Exception { final MultipartFormEntity entity = MultipartEntityBuilder.create() .setBoundary("blah-blah") .setCharset(StandardCharsets.UTF_8) .setLaxMode() .buildEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity.getMultipart() instanceof LegacyMultipart); Assertions.assertEquals("blah-blah", entity.getMultipart().boundary); Assertions.assertEquals(StandardCharsets.UTF_8, entity.getMultipart().charset); } @Test public void testAddBodyParts() throws Exception { final MultipartFormEntity entity = MultipartEntityBuilder.create() .addTextBody("p1", "stuff") .addBinaryBody("p2", new File("stuff")) .addBinaryBody("p3", new byte[]{}) .addBinaryBody("p4", new ByteArrayInputStream(new byte[]{})) .addBinaryBody("p5", new ByteArrayInputStream(new byte[]{}), ContentType.DEFAULT_BINARY, "filename") .buildEntity(); Assertions.assertNotNull(entity); final List bodyParts = entity.getMultipart().getParts(); Assertions.assertNotNull(bodyParts); Assertions.assertEquals(5, bodyParts.size()); } @Test public void testMultipartCustomContentType() throws Exception { final MultipartFormEntity entity = MultipartEntityBuilder.create() .setContentType(ContentType.APPLICATION_XML) .setBoundary("blah-blah") .setCharset(StandardCharsets.UTF_8) .setLaxMode() .buildEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals("application/xml; boundary=blah-blah; charset=UTF-8", entity.getContentType()); } @Test public void testMultipartContentTypeParameter() throws Exception { final MultipartFormEntity entity = MultipartEntityBuilder.create() .setContentType(ContentType.MULTIPART_FORM_DATA.withParameters( new BasicNameValuePair("boundary", "yada-yada"), new BasicNameValuePair("charset", "ascii"))) .buildEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals("multipart/form-data; boundary=yada-yada; charset=US-ASCII", entity.getContentType()); Assertions.assertEquals("yada-yada", entity.getMultipart().boundary); Assertions.assertEquals(StandardCharsets.US_ASCII, entity.getMultipart().charset); } @Test public void testMultipartCustomContentTypeParameterOverrides() throws Exception { final MultipartFormEntity entity = MultipartEntityBuilder.create() .setContentType(ContentType.MULTIPART_FORM_DATA.withParameters( new BasicNameValuePair("boundary", "yada-yada"), new BasicNameValuePair("charset", "ascii"), new BasicNameValuePair("my", "stuff"))) .setBoundary("blah-blah") .setCharset(StandardCharsets.UTF_8) .setLaxMode() .buildEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals("multipart/form-data; boundary=blah-blah; charset=UTF-8; my=stuff", entity.getContentType()); } @Test public void testMultipartCustomContentTypeUsingAddParameter() { final MultipartEntityBuilder eb = MultipartEntityBuilder.create(); eb.setMimeSubtype("related"); eb.addParameter(new BasicNameValuePair("boundary", "yada-yada")); eb.addParameter(new BasicNameValuePair("charset", "ascii")); eb.addParameter(new BasicNameValuePair("my", "stuff")); eb.buildEntity(); final MultipartFormEntity entity = eb.buildEntity(); Assertions.assertNotNull(entity); Assertions.assertEquals("multipart/related; boundary=yada-yada; charset=US-ASCII; my=stuff", entity.getContentType()); } @Test public void testMultipartWriteTo() throws Exception { final String helloWorld = "hello world"; final List parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_NAME, "test")); parameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_FILENAME, helloWorld)); final MultipartFormEntity entity = MultipartEntityBuilder.create() .setStrictMode() .setBoundary("xxxxxxxxxxxxxxxxxxxxxxxx") .addPart(new FormBodyPartBuilder() .setName("test") .setBody(new StringBody("hello world", ContentType.TEXT_PLAIN)) .addField("Content-Disposition", "multipart/form-data", parameters) .build()) .buildEntity(); final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); out.close(); Assertions.assertEquals("--xxxxxxxxxxxxxxxxxxxxxxxx\r\n" + "Content-Disposition: multipart/form-data; name=\"test\"; filename=\"hello world\"\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n" + helloWorld + "\r\n" + "--xxxxxxxxxxxxxxxxxxxxxxxx--\r\n", out.toString(StandardCharsets.US_ASCII.name())); } @Test public void testMultipartWriteToRFC7578Mode() throws Exception { final String helloWorld = "hello \u03BA\u03CC\u03C3\u03BC\u03B5!%"; final List parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_NAME, "test")); parameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_FILENAME, helloWorld)); final MultipartFormEntity entity = MultipartEntityBuilder.create() .setMode(HttpMultipartMode.EXTENDED) .setBoundary("xxxxxxxxxxxxxxxxxxxxxxxx") .addPart(new FormBodyPartBuilder() .setName("test") .setBody(new StringBody(helloWorld, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8))) .addField("Content-Disposition", "multipart/form-data", parameters) .build()) .buildEntity(); final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); out.close(); Assertions.assertEquals("--xxxxxxxxxxxxxxxxxxxxxxxx\r\n" + "Content-Disposition: multipart/form-data; name=\"test\"; filename=\"hello%20%CE%BA%CF%8C%CF%83%CE%BC%CE%B5!%25\"\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" + "\r\n" + "hello \u00ce\u00ba\u00cf\u008c\u00cf\u0083\u00ce\u00bc\u00ce\u00b5!%\r\n" + "--xxxxxxxxxxxxxxxxxxxxxxxx--\r\n", out.toString(StandardCharsets.ISO_8859_1.name())); } @Test public void testMultipartWriteToRFC6532Mode() throws Exception { final String helloWorld = "hello \u03BA\u03CC\u03C3\u03BC\u03B5!%"; final List parameters = new ArrayList<>(); parameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_NAME, "test")); parameters.add(new BasicNameValuePair(MimeConsts.FIELD_PARAM_FILENAME, helloWorld)); final MultipartFormEntity entity = MultipartEntityBuilder.create() .setMode(HttpMultipartMode.EXTENDED) .setContentType(ContentType.create("multipart/other")) .setBoundary("xxxxxxxxxxxxxxxxxxxxxxxx") .addPart(new FormBodyPartBuilder() .setName("test") .setBody(new StringBody(helloWorld, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8))) .addField("Content-Disposition", "multipart/form-data", parameters) .build()) .buildEntity(); final ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); out.close(); Assertions.assertEquals("--xxxxxxxxxxxxxxxxxxxxxxxx\r\n" + "Content-Disposition: multipart/form-data; name=\"test\"; " + "filename=\"hello \u00ce\u00ba\u00cf\u008c\u00cf\u0083\u00ce\u00bc\u00ce\u00b5!%\"\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" + "\r\n" + "hello \u00ce\u00ba\u00cf\u008c\u00cf\u0083\u00ce\u00bc\u00ce\u00b5!%\r\n" + "--xxxxxxxxxxxxxxxxxxxxxxxx--\r\n", out.toString(StandardCharsets.ISO_8859_1.name())); } } TestMultipartForm.java000066400000000000000000000357201434266521000374120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.Writer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMultipartForm { private File tmpfile; @AfterEach public void cleanup() { if (tmpfile != null) { tmpfile.delete(); } } @Test public void testMultipartFormStringParts() throws Exception { final FormBodyPart p1 = FormBodyPartBuilder.create( "field1", new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build(); final FormBodyPart p2 = FormBodyPartBuilder.create( "field2", new StringBody("that stuff", ContentType.create( ContentType.TEXT_PLAIN.getMimeType(), StandardCharsets.UTF_8))).build(); final FormBodyPart p3 = FormBodyPartBuilder.create( "field3", new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT)).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2, p3)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n" + "this stuff\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" + "\r\n" + "that stuff\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field3\"\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n" + "all kind of stuff\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(s.length(), multipart.getTotalLength()); } @Test public void testMultipartFormCustomContentType() throws Exception { final FormBodyPart p1 = FormBodyPartBuilder.create( "field1", new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build(); final FormBodyPart p2 = FormBodyPartBuilder.create( "field2", new StringBody("that stuff", ContentType.parse("stuff/plain; param=value"))).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n" + "this stuff\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "Content-Type: stuff/plain; param=value\r\n" + "\r\n" + "that stuff\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(s.length(), multipart.getTotalLength()); } @Test public void testMultipartFormBinaryParts() throws Exception { tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } final FormBodyPart p1 = FormBodyPartBuilder.create( "field1", new FileBody(tmpfile)).build(); @SuppressWarnings("resource") final FormBodyPart p2 = FormBodyPartBuilder.create( "field2", new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Disposition: form-data; name=\"field1\"; " + "filename=\"" + tmpfile.getName() + "\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field2\"; " + "filename=\"file.tmp\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } @Test public void testMultipartFormStrict() throws Exception { tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } final FormBodyPart p1 = FormBodyPartBuilder.create( "field1", new FileBody(tmpfile)).build(); final FormBodyPart p2 = FormBodyPartBuilder.create( "field2", new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build(); @SuppressWarnings("resource") final FormBodyPart p3 = FormBodyPartBuilder.create( "field3", new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2, p3)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Disposition: form-data; name=\"field1\"; " + "filename=\"" + tmpfile.getName() + "\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field2\"; " + "filename=\"test-file\"\r\n" + "Content-Type: text/plain; charset=US-ASCII\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field3\"; " + "filename=\"file.tmp\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } @Test public void testMultipartFormRFC6532() throws Exception { tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } final FormBodyPart p1 = FormBodyPartBuilder.create( "field1\u0414", new FileBody(tmpfile)).build(); final FormBodyPart p2 = FormBodyPartBuilder.create( "field2", new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build(); @SuppressWarnings("resource") final FormBodyPart p3 = FormBodyPartBuilder.create( "field3", new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build(); final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart(null, "foo", Arrays.asList(p1, p2, p3)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Disposition: form-data; name=\"field1\u0414\"; " + "filename=\"" + tmpfile.getName() + "\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field2\"; " + "filename=\"test-file\"\r\n" + "Content-Type: text/plain; charset=US-ASCII\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field3\"; " + "filename=\"file.tmp\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("UTF-8"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } private static final int SWISS_GERMAN_HELLO [] = { 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4 }; private static final int RUSSIAN_HELLO [] = { 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 0x442 }; private static String constructString(final int [] unicodeChars) { final StringBuilder buffer = new StringBuilder(); if (unicodeChars != null) { for (final int unicodeChar : unicodeChars) { buffer.append((char)unicodeChar); } } return buffer.toString(); } @Test public void testMultipartFormBrowserCompatibleNonASCIIHeaders() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final String s2 = constructString(RUSSIAN_HELLO); tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } @SuppressWarnings("resource") final FormBodyPart p1 = FormBodyPartBuilder.create( "field1", new InputStreamBody(new FileInputStream(tmpfile), s1 + ".tmp")).build(); @SuppressWarnings("resource") final FormBodyPart p2 = FormBodyPartBuilder.create( "field2", new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp")).build(); final LegacyMultipart multipart = new LegacyMultipart( StandardCharsets.UTF_8, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Disposition: form-data; name=\"field1\"; " + "filename=\"" + s1 + ".tmp\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field2\"; " + "filename=\"" + s2 + ".tmp\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("UTF-8"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } @Test public void testMultipartFormStringPartsMultiCharsets() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final String s2 = constructString(RUSSIAN_HELLO); final FormBodyPart p1 = FormBodyPartBuilder.create( "field1", new StringBody(s1, ContentType.create("text/plain", StandardCharsets.ISO_8859_1))).build(); final FormBodyPart p2 = FormBodyPartBuilder.create( "field2", new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R")))).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out1 = new ByteArrayOutputStream(); multipart.writeTo(out1); out1.close(); final ByteArrayOutputStream out2 = new ByteArrayOutputStream(); out2.write(( "--foo\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n").getBytes(StandardCharsets.US_ASCII)); out2.write(s1.getBytes(StandardCharsets.ISO_8859_1)); out2.write(("\r\n" + "--foo\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "Content-Type: text/plain; charset=KOI8-R\r\n" + "\r\n").getBytes(StandardCharsets.US_ASCII)); out2.write(s2.getBytes(Charset.forName("KOI8-R"))); out2.write(("\r\n" + "--foo--\r\n").getBytes(StandardCharsets.US_ASCII)); out2.close(); final byte[] actual = out1.toByteArray(); final byte[] expected = out2.toByteArray(); Assertions.assertEquals(expected.length, actual.length); for (int i = 0; i < actual.length; i++) { Assertions.assertEquals(expected[i], actual[i]); } Assertions.assertEquals(expected.length, multipart.getTotalLength()); } } TestMultipartFormHttpEntity.java000066400000000000000000000125411434266521000414430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicHeaderValueParser; import org.apache.hc.core5.http.message.ParserCursor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMultipartFormHttpEntity { @Test public void testExplictContractorParams() throws Exception { final HttpEntity entity = MultipartEntityBuilder.create() .setLaxMode() .setBoundary("whatever") .setCharset(StandardCharsets.UTF_8) .build(); Assertions.assertNull(entity.getContentEncoding()); final String contentType = entity.getContentType(); final HeaderElement elem = BasicHeaderValueParser.INSTANCE.parseHeaderElement(contentType, new ParserCursor(0, contentType.length())); Assertions.assertEquals("multipart/mixed", elem.getName()); final NameValuePair p1 = elem.getParameterByName("boundary"); Assertions.assertNotNull(p1); Assertions.assertEquals("whatever", p1.getValue()); final NameValuePair p2 = elem.getParameterByName("charset"); Assertions.assertNotNull(p2); Assertions.assertEquals("UTF-8", p2.getValue()); } @Test public void testImplictContractorParams() throws Exception { final HttpEntity entity = MultipartEntityBuilder.create().build(); Assertions.assertNull(entity.getContentEncoding()); final String contentType = entity.getContentType(); final HeaderElement elem = BasicHeaderValueParser.INSTANCE.parseHeaderElement(contentType, new ParserCursor(0, contentType.length())); Assertions.assertEquals("multipart/mixed", elem.getName()); final NameValuePair p1 = elem.getParameterByName("boundary"); Assertions.assertNotNull(p1); final String boundary = p1.getValue(); Assertions.assertNotNull(boundary); Assertions.assertTrue(boundary.length() >= 30); Assertions.assertTrue(boundary.length() <= 40); final NameValuePair p2 = elem.getParameterByName("charset"); Assertions.assertNull(p2); } @Test public void testRepeatable() throws Exception { final HttpEntity entity = MultipartEntityBuilder.create() .addTextBody("p1", "blah blah", ContentType.DEFAULT_TEXT) .addTextBody("p2", "yada yada", ContentType.DEFAULT_TEXT) .build(); Assertions.assertTrue(entity.isRepeatable()); Assertions.assertFalse(entity.isChunked()); Assertions.assertFalse(entity.isStreaming()); final long len = entity.getContentLength(); Assertions.assertEquals(len, entity.getContentLength()); ByteArrayOutputStream out = new ByteArrayOutputStream(); entity.writeTo(out); out.close(); byte[] bytes = out.toByteArray(); Assertions.assertNotNull(bytes); Assertions.assertEquals(bytes.length, len); Assertions.assertEquals(len, entity.getContentLength()); out = new ByteArrayOutputStream(); entity.writeTo(out); out.close(); bytes = out.toByteArray(); Assertions.assertNotNull(bytes); Assertions.assertEquals(bytes.length, len); } @Test public void testNonRepeatable() throws Exception { final HttpEntity entity = MultipartEntityBuilder.create() .addPart("p1", new InputStreamBody( new ByteArrayInputStream("blah blah".getBytes()), ContentType.DEFAULT_BINARY)) .addPart("p2", new InputStreamBody( new ByteArrayInputStream("yada yada".getBytes()), ContentType.DEFAULT_BINARY)) .build(); Assertions.assertFalse(entity.isRepeatable()); Assertions.assertTrue(entity.isChunked()); Assertions.assertTrue(entity.isStreaming()); Assertions.assertEquals(-1, entity.getContentLength()); } } TestMultipartFormat.java000066400000000000000000000042761434266521000377410ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMultipartFormat { @Test public void testLineBreak() { Assertions.assertTrue(AbstractMultipartFormat.isLineBreak('\r')); Assertions.assertTrue(AbstractMultipartFormat.isLineBreak('\n')); Assertions.assertTrue(AbstractMultipartFormat.isLineBreak('\f')); Assertions.assertTrue(AbstractMultipartFormat.isLineBreak((char) 11)); Assertions.assertFalse(AbstractMultipartFormat.isLineBreak(' ')); Assertions.assertFalse(AbstractMultipartFormat.isLineBreak('x')); } @Test public void testLineBreakRewrite() { final String s = "blah blah blah"; Assertions.assertSame(s, AbstractMultipartFormat.stripLineBreaks(s)); Assertions.assertEquals("blah blah blah ", AbstractMultipartFormat.stripLineBreaks("blah\rblah\nblah\f")); Assertions.assertEquals(" f", AbstractMultipartFormat.stripLineBreaks("\r\n\r\nf")); } }TestMultipartMixed.java000066400000000000000000000316301434266521000375510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.Writer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMultipartMixed { private File tmpfile; @AfterEach public void cleanup() { if (tmpfile != null) { tmpfile.delete(); } } @Test public void testMultipartPartStringParts() throws Exception { final MultipartPart p1 = MultipartPartBuilder.create( new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build(); final MultipartPart p2 = MultipartPartBuilder.create( new StringBody("that stuff", ContentType.create( ContentType.TEXT_PLAIN.getMimeType(), StandardCharsets.UTF_8))).build(); final MultipartPart p3 = MultipartPartBuilder.create( new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT)).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2, p3)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n" + "this stuff\r\n" + "--foo\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" + "\r\n" + "that stuff\r\n" + "--foo\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n" + "all kind of stuff\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(s.length(), multipart.getTotalLength()); } @Test public void testMultipartPartCustomContentType() throws Exception { final MultipartPart p1 = MultipartPartBuilder.create( new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build(); final MultipartPart p2 = MultipartPartBuilder.create( new StringBody("that stuff", ContentType.parse("stuff/plain; param=value"))).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n" + "this stuff\r\n" + "--foo\r\n" + "Content-Type: stuff/plain; param=value\r\n" + "\r\n" + "that stuff\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(s.length(), multipart.getTotalLength()); } @Test public void testMultipartPartBinaryParts() throws Exception { tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } final MultipartPart p1 = MultipartPartBuilder.create( new FileBody(tmpfile)).build(); @SuppressWarnings("resource") final MultipartPart p2 = MultipartPartBuilder.create( new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } @Test public void testMultipartPartStrict() throws Exception { tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } final MultipartPart p1 = MultipartPartBuilder.create( new FileBody(tmpfile)).build(); final MultipartPart p2 = MultipartPartBuilder.create( new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build(); @SuppressWarnings("resource") final MultipartPart p3 = MultipartPartBuilder.create( new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2, p3)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Type: text/plain; charset=US-ASCII\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("US-ASCII"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } @Test public void testMultipartPartRFC6532() throws Exception { tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } final MultipartPart p1 = MultipartPartBuilder.create( new FileBody(tmpfile)).build(); final MultipartPart p2 = MultipartPartBuilder.create( new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build(); @SuppressWarnings("resource") final MultipartPart p3 = MultipartPartBuilder.create( new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build(); final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart(null, "foo", Arrays.asList(p1, p2, p3)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Type: text/plain; charset=US-ASCII\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("UTF-8"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } private static final int SWISS_GERMAN_HELLO [] = { 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4 }; private static final int RUSSIAN_HELLO [] = { 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 0x442 }; private static String constructString(final int [] unicodeChars) { final StringBuilder buffer = new StringBuilder(); if (unicodeChars != null) { for (final int unicodeChar : unicodeChars) { buffer.append((char)unicodeChar); } } return buffer.toString(); } @Test public void testMultipartPartBrowserCompatibleNonASCIIHeaders() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final String s2 = constructString(RUSSIAN_HELLO); tmpfile = File.createTempFile("tmp", ".bin"); try (Writer writer = new FileWriter(tmpfile)) { writer.append("some random whatever"); } @SuppressWarnings("resource") final MultipartPart p1 = MultipartPartBuilder.create( new InputStreamBody(new FileInputStream(tmpfile), s1 + ".tmp")).build(); @SuppressWarnings("resource") final MultipartPart p2 = MultipartPartBuilder.create( new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp")).build(); final LegacyMultipart multipart = new LegacyMultipart( StandardCharsets.UTF_8, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out = new ByteArrayOutputStream(); multipart.writeTo(out); out.close(); final String expected = "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n" + "some random whatever\r\n" + "--foo--\r\n"; final String s = out.toString("UTF-8"); Assertions.assertEquals(expected, s); Assertions.assertEquals(-1, multipart.getTotalLength()); } @Test public void testMultipartPartStringPartsMultiCharsets() throws Exception { final String s1 = constructString(SWISS_GERMAN_HELLO); final String s2 = constructString(RUSSIAN_HELLO); final MultipartPart p1 = MultipartPartBuilder.create( new StringBody(s1, ContentType.create("text/plain", StandardCharsets.ISO_8859_1))).build(); final MultipartPart p2 = MultipartPartBuilder.create( new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R")))).build(); final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo", Arrays.asList(p1, p2)); final ByteArrayOutputStream out1 = new ByteArrayOutputStream(); multipart.writeTo(out1); out1.close(); final ByteArrayOutputStream out2 = new ByteArrayOutputStream(); out2.write(( "--foo\r\n" + "Content-Type: text/plain; charset=ISO-8859-1\r\n" + "\r\n").getBytes(StandardCharsets.US_ASCII)); out2.write(s1.getBytes(StandardCharsets.ISO_8859_1)); out2.write(("\r\n" + "--foo\r\n" + "Content-Type: text/plain; charset=KOI8-R\r\n" + "\r\n").getBytes(StandardCharsets.US_ASCII)); out2.write(s2.getBytes(Charset.forName("KOI8-R"))); out2.write(("\r\n" + "--foo--\r\n").getBytes(StandardCharsets.US_ASCII)); out2.close(); final byte[] actual = out1.toByteArray(); final byte[] expected = out2.toByteArray(); Assertions.assertEquals(expected.length, actual.length); for (int i = 0; i < actual.length; i++) { Assertions.assertEquals(expected[i], actual[i]); } Assertions.assertEquals(expected.length, multipart.getTotalLength()); } } TestMultipartPartBuilder.java000066400000000000000000000150071434266521000407200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/entity/mime/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.entity.mime; import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.hc.core5.http.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestMultipartPartBuilder { @Test public void testBuildBodyPartBasics() throws Exception { final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN); final MultipartPart part = MultipartPartBuilder.create() .setBody(stringBody) .build(); Assertions.assertNotNull(part); Assertions.assertEquals(stringBody, part.getBody()); final Header header = part.getHeader(); Assertions.assertNotNull(header); assertFields(Collections.singletonList( new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header.getFields()); } @Test public void testBuildBodyPartMultipleBuilds() throws Exception { final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN); final MultipartPartBuilder builder = MultipartPartBuilder.create(); final MultipartPart part1 = builder .setBody(stringBody) .build(); Assertions.assertNotNull(part1); Assertions.assertEquals(stringBody, part1.getBody()); final Header header1 = part1.getHeader(); Assertions.assertNotNull(header1); assertFields(Collections.singletonList( new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header1.getFields()); final FileBody fileBody = new FileBody(new File("/path/stuff.bin"), ContentType.DEFAULT_BINARY); final MultipartPart part2 = builder .setBody(fileBody) .build(); Assertions.assertNotNull(part2); Assertions.assertEquals(fileBody, part2.getBody()); final Header header2 = part2.getHeader(); Assertions.assertNotNull(header2); assertFields(Collections.singletonList( new MimeField("Content-Type", "application/octet-stream")), header2.getFields()); } @Test public void testBuildBodyPartCustomHeaders() throws Exception { final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN); final MultipartPartBuilder builder = MultipartPartBuilder.create(stringBody); final MultipartPart part1 = builder .addHeader("header1", "blah") .addHeader("header3", "blah") .addHeader("header3", "blah") .addHeader("header3", "blah") .addHeader("header3", "blah") .addHeader("header3", "blah") .build(); Assertions.assertNotNull(part1); final Header header1 = part1.getHeader(); Assertions.assertNotNull(header1); assertFields(Arrays.asList( new MimeField("header1", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("header3", "blah"), new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header1.getFields()); final MultipartPart part2 = builder .addHeader("header2", "yada") .removeHeaders("header3") .build(); Assertions.assertNotNull(part2); final Header header2 = part2.getHeader(); Assertions.assertNotNull(header2); assertFields(Arrays.asList( new MimeField("header1", "blah"), new MimeField("header2", "yada"), new MimeField("Content-Type", "text/plain; charset=ISO-8859-1")), header2.getFields()); final MultipartPart part3 = builder .addHeader("Content-Disposition", "disposition stuff") .addHeader("Content-Type", "type stuff") .addHeader("Content-Transfer-Encoding", "encoding stuff") .build(); Assertions.assertNotNull(part3); final Header header3 = part3.getHeader(); Assertions.assertNotNull(header3); assertFields(Arrays.asList( new MimeField("header1", "blah"), new MimeField("header2", "yada"), new MimeField("Content-Disposition", "disposition stuff"), new MimeField("Content-Type", "type stuff"), new MimeField("Content-Transfer-Encoding", "encoding stuff")), header3.getFields()); } private static void assertFields(final List expected, final List result) { Assertions.assertNotNull(result); Assertions.assertEquals(expected.size(), result.size()); for (int i = 0; i < expected.size(); i++) { final MimeField expectedField = expected.get(i); final MimeField resultField = result.get(i); Assertions.assertNotNull(resultField); Assertions.assertEquals(expectedField.getName(), resultField.getName()); Assertions.assertEquals(expectedField.getBody(), resultField.getBody()); } } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/000077500000000000000000000000001434266521000325255ustar00rootroot00000000000000AsyncClientAuthentication.java000066400000000000000000000073361434266521000404360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; /** * 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(final String[] args) throws Exception { final CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom() .setDefaultCredentialsProvider(CredentialsProviderBuilder.create() .add(new HttpHost("httpbin.org", 80), "user", "passwd".toCharArray()) .build()) .build(); httpclient.start(); final SimpleHttpRequest request = SimpleRequestBuilder.get("http://httpbin.org/basic-auth/user/passwd") .build(); System.out.println("Executing request " + request); final Future future = httpclient.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future.get(); System.out.println("Shutting down"); httpclient.close(CloseMode.GRACEFUL); } } AsyncClientConnectionConfig.java000066400000000000000000000137741434266521000407070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how to use connection configuration on a per-route or a per-host * basis. */ public class AsyncClientConnectionConfig { public static void main(final String[] args) throws Exception { final PoolingAsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder.create() .setConnectionConfigResolver(route -> { // Use different settings for all secure (TLS) connections final HttpHost targetHost = route.getTargetHost(); if (route.isSecure()) { return ConnectionConfig.custom() .setConnectTimeout(Timeout.ofMinutes(2)) .setSocketTimeout(Timeout.ofMinutes(2)) .setValidateAfterInactivity(TimeValue.ofMinutes(1)) .setTimeToLive(TimeValue.ofHours(1)) .build(); } else { return ConnectionConfig.custom() .setConnectTimeout(Timeout.ofMinutes(1)) .setSocketTimeout(Timeout.ofMinutes(1)) .setValidateAfterInactivity(TimeValue.ofSeconds(15)) .setTimeToLive(TimeValue.ofMinutes(15)) .build(); } }) .setTlsConfigResolver(host -> { // Use different settings for specific hosts if (host.getSchemeName().equalsIgnoreCase("httpbin.org")) { return TlsConfig.custom() .setSupportedProtocols(TLS.V_1_3) .setHandshakeTimeout(Timeout.ofSeconds(10)) .build(); } else { return TlsConfig.DEFAULT; } }) .build(); try (final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setConnectionManager(cm) .build()) { client.start(); for (final URIScheme uriScheme : URIScheme.values()) { final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(new HttpHost(uriScheme.id, "httpbin.org")) .setPath("/headers") .build(); System.out.println("Executing request " + request); final Future future = client.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future.get(); } System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } } AsyncClientConnectionEviction.java000066400000000000000000000115461434266521000412550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * Example demonstrating how to evict expired and idle connections * from the connection pool. */ public class AsyncClientConnectionEviction { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(Timeout.ofSeconds(5)) .build(); final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setIOReactorConfig(ioReactorConfig) .evictExpiredConnections() .evictIdleConnections(TimeValue.ofSeconds(10)) .build(); client.start(); final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(new HttpHost("httpbin.org")) .setPath("/") .build(); System.out.println("Executing request " + request); final Future future1 = client.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future1.get(); Thread.sleep(TimeUnit.SECONDS.toMillis(30)); // Previous connection should get evicted from the pool by now final Future future2 = client.execute( request, new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future2.get(); System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientCustomSSL.java000066400000000000000000000124621434266521000373070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.security.cert.X509Certificate; import java.util.concurrent.Future; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.ssl.SSLContexts; /** * This example demonstrates how to create secure connections with a custom SSL * context. */ public class AsyncClientCustomSSL { public static void main(final String[] args) throws Exception { // Trust standard CA and those trusted by our custom strategy final SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial((chain, authType) -> { final X509Certificate cert = chain[0]; return "CN=httpbin.org".equalsIgnoreCase(cert.getSubjectDN().getName()); }) .build(); final TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create() .setSslContext(sslcontext) .build(); final PoolingAsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder.create() .setTlsStrategy(tlsStrategy) .build(); try (final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setConnectionManager(cm) .build()) { client.start(); final HttpHost target = new HttpHost("https", "httpbin.org"); final HttpClientContext clientContext = HttpClientContext.create(); final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(target) .setPath("/") .build(); System.out.println("Executing request " + request); final Future future = client.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), clientContext, new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); final SSLSession sslSession = clientContext.getSSLSession(); if (sslSession != null) { System.out.println("SSL protocol " + sslSession.getProtocol()); System.out.println("SSL cipher suite " + sslSession.getCipherSuite()); } System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future.get(); System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } } AsyncClientFullDuplexExchange.java000066400000000000000000000140121434266521000411730ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; /** * This example demonstrates a full-duplex, streaming HTTP/1.1 message exchange. */ public class AsyncClientFullDuplexExchange { public static void main(final String[] args) throws Exception { final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal( H2Config.DEFAULT, Http1Config.DEFAULT, IOReactorConfig.DEFAULT); client.start(); final BasicHttpRequest request = BasicRequestBuilder.post("http://httpbin.org/post").build(); final BasicRequestProducer requestProducer = new BasicRequestProducer(request, new BasicAsyncEntityProducer("stuff", ContentType.TEXT_PLAIN)); final BasicResponseConsumer responseConsumer = new BasicResponseConsumer<>( new StringAsyncEntityConsumer()); System.out.println("Executing request " + request); final CountDownLatch latch = new CountDownLatch(1); client.execute(new AsyncClientExchangeHandler() { @Override public void releaseResources() { requestProducer.releaseResources(); responseConsumer.releaseResources(); latch.countDown(); } @Override public void cancel() { System.out.println(request + " cancelled"); } @Override public void failed(final Exception cause) { System.out.println(request + "->" + cause); } @Override public void produceRequest(final RequestChannel channel, final HttpContext context) throws HttpException, IOException { requestProducer.sendRequest(channel, context); } @Override public int available() { return requestProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { requestProducer.produce(channel); } @Override public void consumeInformation( final HttpResponse response, final HttpContext context) throws HttpException, IOException { System.out.println(request + "->" + new StatusLine(response)); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { System.out.println(request + "->" + new StatusLine(response)); responseConsumer.consumeResponse(response, entityDetails, context, null); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { responseConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { responseConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { responseConsumer.streamEnd(trailers); } }); latch.await(1, TimeUnit.MINUTES); System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientH2FullDuplexExchange.java000066400000000000000000000147641434266521000414030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler; import org.apache.hc.core5.http.nio.CapacityChannel; import org.apache.hc.core5.http.nio.DataStreamChannel; import org.apache.hc.core5.http.nio.RequestChannel; import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer; import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; /** * This example demonstrates a full-duplex, streaming HTTP/2 message exchange. */ public class AsyncClientH2FullDuplexExchange { public static void main(final String[] args) throws Exception { final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal( H2Config.DEFAULT, Http1Config.DEFAULT, IOReactorConfig.DEFAULT, PoolingAsyncClientConnectionManagerBuilder.create() .setDefaultTlsConfig(TlsConfig.custom() .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .build()) .build()); client.start(); final BasicHttpRequest request = BasicRequestBuilder.post("https://nghttp2.org/httpbin/post").build(); final BasicRequestProducer requestProducer = new BasicRequestProducer(request, new BasicAsyncEntityProducer("stuff", ContentType.TEXT_PLAIN)); final BasicResponseConsumer responseConsumer = new BasicResponseConsumer<>( new StringAsyncEntityConsumer()); System.out.println("Executing request " + request); final CountDownLatch latch = new CountDownLatch(1); client.execute(new AsyncClientExchangeHandler() { @Override public void releaseResources() { requestProducer.releaseResources(); responseConsumer.releaseResources(); latch.countDown(); } @Override public void cancel() { System.out.println(request + " cancelled"); } @Override public void failed(final Exception cause) { System.out.println(request + "->" + cause); } @Override public void produceRequest(final RequestChannel channel, final HttpContext context) throws HttpException, IOException { requestProducer.sendRequest(channel, context); } @Override public int available() { return requestProducer.available(); } @Override public void produce(final DataStreamChannel channel) throws IOException { requestProducer.produce(channel); } @Override public void consumeInformation( final HttpResponse response, final HttpContext context) throws HttpException, IOException { System.out.println(request + "->" + new StatusLine(response)); } @Override public void consumeResponse( final HttpResponse response, final EntityDetails entityDetails, final HttpContext context) throws HttpException, IOException { System.out.println(request + "->" + new StatusLine(response)); responseConsumer.consumeResponse(response, entityDetails, context, null); } @Override public void updateCapacity(final CapacityChannel capacityChannel) throws IOException { responseConsumer.updateCapacity(capacityChannel); } @Override public void consume(final ByteBuffer src) throws IOException { responseConsumer.consume(src); } @Override public void streamEnd(final List trailers) throws HttpException, IOException { responseConsumer.streamEnd(trailers); } }); latch.await(1, TimeUnit.MINUTES); System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientH2Multiplexing.java000066400000000000000000000122271434266521000403250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; /** * This example demonstrates concurrent (multiplexed) execution of multiple * HTTP/2 message exchanges. */ public class AsyncClientH2Multiplexing { public static void main(final String[] args) throws Exception { final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal( H2Config.DEFAULT, Http1Config.DEFAULT, IOReactorConfig.DEFAULT, PoolingAsyncClientConnectionManagerBuilder.create() .setDefaultTlsConfig(TlsConfig.custom() .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .build()) .build()); client.start(); final HttpHost target = new HttpHost("https", "nghttp2.org"); final Future leaseFuture = client.lease(target, null); final AsyncClientEndpoint endpoint = leaseFuture.get(30, TimeUnit.SECONDS); try { final String[] requestUris = new String[] {"/httpbin/ip", "/httpbin/user-agent", "/httpbin/headers"}; final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(); System.out.println("Executing request " + request); endpoint.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { latch.countDown(); System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { latch.countDown(); System.out.println(request + "->" + ex); } @Override public void cancelled() { latch.countDown(); System.out.println(request + " cancelled"); } }); } latch.await(); } finally { endpoint.releaseAndReuse(); } System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientH2ServerPush.java000066400000000000000000000131111434266521000377430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.AbstractBinPushConsumer; import org.apache.hc.client5.http.async.methods.AbstractCharResponseConsumer; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.io.CloseMode; /** * This example demonstrates handling of HTTP/2 message exchanges pushed by the server. */ public class AsyncClientH2ServerPush { public static void main(final String[] args) throws Exception { final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setH2Config(H2Config.custom() .setPushEnabled(true) .build()) .setConnectionManager(PoolingAsyncClientConnectionManagerBuilder.create() .setDefaultTlsConfig(TlsConfig.custom() .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2) .build()) .build()) .build(); client.start(); client.register("*", () -> new AbstractBinPushConsumer() { @Override protected void start( final HttpRequest promise, final HttpResponse response, final ContentType contentType) throws HttpException, IOException { System.out.println(promise.getPath() + " (push)->" + new StatusLine(response)); } @Override protected int capacityIncrement() { return Integer.MAX_VALUE; } @Override protected void data(final ByteBuffer data, final boolean endOfStream) throws IOException { } @Override protected void completed() { } @Override public void failed(final Exception cause) { System.out.println("(push)->" + cause); } @Override public void releaseResources() { } }); final BasicHttpRequest request = BasicRequestBuilder.get("https://nghttp2.org/httpbin/").build(); System.out.println("Executing request " + request); final Future future = client.execute( new BasicRequestProducer(request, null), new AbstractCharResponseConsumer() { @Override protected void start( final HttpResponse response, final ContentType contentType) throws HttpException, IOException { System.out.println(request + "->" + new StatusLine(response)); } @Override protected int capacityIncrement() { return Integer.MAX_VALUE; } @Override protected void data(final CharBuffer data, final boolean endOfStream) throws IOException { } @Override protected Void buildResult() throws IOException { return null; } @Override public void failed(final Exception cause) { System.out.println(request + "->" + cause); } @Override public void releaseResources() { } }, null); future.get(); System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientHttp1Pipelining.java000066400000000000000000000121541434266521000404700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; /** * This example demonstrates pipelined execution of multiple HTTP/1.1 message exchanges. */ public class AsyncClientHttp1Pipelining { public static void main(final String[] args) throws Exception { final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal( H2Config.DEFAULT, Http1Config.DEFAULT, IOReactorConfig.DEFAULT, PoolingAsyncClientConnectionManagerBuilder.create() .setDefaultTlsConfig(TlsConfig.custom() .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1) .build()) .build()); client.start(); final HttpHost target = new HttpHost("httpbin.org"); final Future leaseFuture = client.lease(target, null); final AsyncClientEndpoint endpoint = leaseFuture.get(30, TimeUnit.SECONDS); try { final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"}; final CountDownLatch latch = new CountDownLatch(requestUris.length); for (final String requestUri: requestUris) { final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(); System.out.println("Executing request " + request); endpoint.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { latch.countDown(); System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { latch.countDown(); System.out.println(request + "->" + ex); } @Override public void cancelled() { latch.countDown(); System.out.println(request + " cancelled"); } }); } latch.await(); } finally { endpoint.releaseAndReuse(); } System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientHttpExchange.java000066400000000000000000000077531434266521000400440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * Example of asynchronous HTTP/1.1 request execution. */ public class AsyncClientHttpExchange { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(Timeout.ofSeconds(5)) .build(); final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setIOReactorConfig(ioReactorConfig) .build(); client.start(); final HttpHost target = new HttpHost("httpbin.org"); final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"}; for (final String requestUri: requestUris) { final SimpleHttpRequest request = SimpleRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(); System.out.println("Executing request " + request); final Future future = client.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future.get(); } System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientHttpExchangeStreaming.java000066400000000000000000000114531434266521000417060ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.IOException; import java.nio.CharBuffer; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.AbstractCharResponseConsumer; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * Example of asynchronous HTTP/1.1 request execution with response streaming. */ public class AsyncClientHttpExchangeStreaming { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(Timeout.ofSeconds(5)) .build(); final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setIOReactorConfig(ioReactorConfig) .build(); client.start(); final HttpHost target = new HttpHost("httpbin.org"); final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"}; for (final String requestUri: requestUris) { final BasicHttpRequest request = BasicRequestBuilder.get() .setHttpHost(target) .setPath(requestUri) .build(); System.out.println("Executing request " + request); final Future future = client.execute( new BasicRequestProducer(request, null), new AbstractCharResponseConsumer() { @Override protected void start( final HttpResponse response, final ContentType contentType) throws HttpException, IOException { System.out.println(request + "->" + new StatusLine(response)); } @Override protected int capacityIncrement() { return Integer.MAX_VALUE; } @Override protected void data(final CharBuffer data, final boolean endOfStream) throws IOException { while (data.hasRemaining()) { System.out.print(data.get()); } if (endOfStream) { System.out.println(); } } @Override protected Void buildResult() throws IOException { return null; } @Override public void failed(final Exception cause) { System.out.println(request + "->" + cause); } @Override public void releaseResources() { } }, null); future.get(); } System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientInterceptors.java000066400000000000000000000142421434266521000401320ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.impl.BasicEntityDetails; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.AsyncDataConsumer; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how to insert custom request interceptor and an execution interceptor * to the request execution chain. */ public class AsyncClientInterceptors { public static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(Timeout.ofSeconds(5)) .build(); final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setIOReactorConfig(ioReactorConfig) // Add a simple request ID to each outgoing request .addRequestInterceptorFirst(new HttpRequestInterceptor() { private final AtomicLong count = new AtomicLong(0); @Override public void process( final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { request.setHeader("request-id", Long.toString(count.incrementAndGet())); } }) // Simulate a 404 response for some requests without passing the message down to the backend .addExecInterceptorAfter(ChainElement.PROTOCOL.name(), "custom", (request, requestEntityProducer, scope, chain, asyncExecCallback) -> { final Header idHeader = request.getFirstHeader("request-id"); if (idHeader != null && "13".equalsIgnoreCase(idHeader.getValue())) { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_NOT_FOUND, "Oppsie"); final ByteBuffer content = ByteBuffer.wrap("bad luck".getBytes(StandardCharsets.US_ASCII)); final AsyncDataConsumer asyncDataConsumer = asyncExecCallback.handleResponse( response, new BasicEntityDetails(content.remaining(), ContentType.TEXT_PLAIN)); asyncDataConsumer.consume(content); asyncDataConsumer.streamEnd(null); } else { chain.proceed(request, requestEntityProducer, scope, asyncExecCallback); } }) .build(); client.start(); final String requestUri = "http://httpbin.org/get"; for (int i = 0; i < 20; i++) { final SimpleHttpRequest request = SimpleRequestBuilder.get(requestUri).build(); System.out.println("Executing request " + request); final Future future = client.execute( request, new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future.get(); } System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncClientMessageTrailers.java000066400000000000000000000106451434266521000405460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.Future; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.nio.entity.DigestingEntityProducer; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how to use a custom execution interceptor * to add trailers to all outgoing request enclosing an entity. */ public class AsyncClientMessageTrailers { public final static void main(final String[] args) throws Exception { final IOReactorConfig ioReactorConfig = IOReactorConfig.custom() .setSoTimeout(Timeout.ofSeconds(5)) .build(); final CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setIOReactorConfig(ioReactorConfig) .addExecInterceptorAfter(ChainElement.PROTOCOL.name(), "custom", (request, entityProducer, scope, chain, asyncExecCallback) -> { // Send MD5 hash in a trailer by decorating the original entity producer chain.proceed( request, entityProducer != null ? new DigestingEntityProducer("MD5", entityProducer) : null, scope, asyncExecCallback); }) .build(); client.start(); final SimpleHttpRequest request = SimpleRequestBuilder.post("http://httpbin.org/post") .setBody("some stuff", ContentType.TEXT_PLAIN) .build(); System.out.println("Executing request " + request); final Future future = client.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future.get(); System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } AsyncPreemptiveBasicClientAuthentication.java000066400000000000000000000100311434266521000434230ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.Future; import org.apache.hc.client5.http.ContextBuilder; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.io.CloseMode; /** * A simple example that uses HttpClient to execute an HTTP request against * a target site that requires user authentication. */ public class AsyncPreemptiveBasicClientAuthentication { public static void main(final String[] args) throws Exception { final CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault(); httpclient.start(); final HttpClientContext localContext = ContextBuilder.create() .preemptiveBasicAuth(new HttpHost("http", "httpbin.org"), new UsernamePasswordCredentials("user", "passwd".toCharArray())) .build(); final SimpleHttpRequest request = SimpleRequestBuilder.get("http://httpbin.org:80/basic-auth/user/passwd") .build(); System.out.println("Executing request " + request); for (int i = 0; i < 3; i++) { final Future future = httpclient.execute( SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), localContext, new FutureCallback() { @Override public void completed(final SimpleHttpResponse response) { System.out.println(request + "->" + new StatusLine(response)); System.out.println(response.getBody()); } @Override public void failed(final Exception ex) { System.out.println(request + "->" + ex); } @Override public void cancelled() { System.out.println(request + " cancelled"); } }); future.get(); } System.out.println("Shutting down"); httpclient.close(CloseMode.GRACEFUL); } } ClientAbortMethod.java000066400000000000000000000051671434266521000366710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * This example demonstrates how to abort an HTTP method before its normal completion. */ public class ClientAbortMethod { public static void main(final String[] args) throws Exception { try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { final HttpGet httpget = new HttpGet("http://httpbin.org/get"); final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); // Cancel the request after once second executorService.schedule(httpget::cancel, 1, TimeUnit.SECONDS); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } ClientAuthentication.java000066400000000000000000000052431434266521000374330ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * A simple example that uses HttpClient to execute an HTTP request against * a target site that requires user authentication. */ public class ClientAuthentication { public static void main(final String[] args) throws Exception { try (final CloseableHttpClient httpclient = HttpClients.custom() .setDefaultCredentialsProvider(CredentialsProviderBuilder.create() .add(new HttpHost("httpbin.org", 80), "user", "passwd".toCharArray()) .build()) .build()) { final HttpGet httpget = new HttpGet("http://httpbin.org/basic-auth/user/passwd"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } ClientChunkEncodedPost.java000066400000000000000000000061701434266521000376540ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.File; import java.io.FileInputStream; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.message.StatusLine; /** * Example how to use unbuffered chunk-encoded POST request. */ public class ClientChunkEncodedPost { public static void main(final String[] args) throws Exception { if (args.length != 1) { System.out.println("File path not given"); System.exit(1); } try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { final HttpPost httppost = new HttpPost("http://httpbin.org/post"); final File file = new File(args[0]); final InputStreamEntity reqEntity = new InputStreamEntity( new FileInputStream(file), -1, ContentType.APPLICATION_OCTET_STREAM); // It may be more appropriate to use FileEntity class in this particular // instance but we are using a more generic InputStreamEntity to demonstrate // the capability to stream out data from any arbitrary source // // FileEntity entity = new FileEntity(file, "binary/octet-stream"); httppost.setEntity(reqEntity); System.out.println("Executing request " + httppost.getMethod() + " " + httppost.getUri()); httpclient.execute(httppost, response -> { System.out.println("----------------------------------------"); System.out.println(httppost + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } ClientConfiguration.java000066400000000000000000000311141434266521000372570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import javax.net.ssl.SSLContext; import org.apache.hc.client5.http.ContextBuilder; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SystemDefaultDnsResolver; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.ManagedHttpClientConnectionFactory; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.config.CharCodingConfig; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory; import org.apache.hc.core5.http.impl.io.DefaultHttpRequestWriterFactory; import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParser; import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParserFactory; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.HttpMessageParser; import org.apache.hc.core5.http.io.HttpMessageParserFactory; import org.apache.hc.core5.http.io.HttpMessageWriterFactory; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicLineParser; import org.apache.hc.core5.http.message.LineParser; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.pool.PoolConcurrencyPolicy; import org.apache.hc.core5.pool.PoolReusePolicy; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.CharArrayBuffer; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how to customize and configure the most common aspects * of HTTP request execution and connection management. */ public class ClientConfiguration { public final static void main(final 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. final HttpMessageParserFactory responseParserFactory = new DefaultHttpResponseParserFactory() { @Override public HttpMessageParser create(final Http1Config h1Config) { final LineParser lineParser = new BasicLineParser() { @Override public Header parseHeader(final CharArrayBuffer buffer) { try { return super.parseHeader(buffer); } catch (final ParseException ex) { return new BasicHeader(buffer.toString(), null); } } }; return new DefaultHttpResponseParser(lineParser, DefaultClassicHttpResponseFactory.INSTANCE, h1Config); } }; final HttpMessageWriterFactory requestWriterFactory = new DefaultHttpRequestWriterFactory(); // Create HTTP/1.1 protocol configuration final Http1Config h1Config = Http1Config.custom() .setMaxHeaderCount(200) .setMaxLineLength(2000) .build(); // Create connection configuration final CharCodingConfig connectionConfig = CharCodingConfig.custom() .setMalformedInputAction(CodingErrorAction.IGNORE) .setUnmappableInputAction(CodingErrorAction.IGNORE) .setCharset(StandardCharsets.UTF_8) .build(); // 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. final HttpConnectionFactory connFactory = new ManagedHttpClientConnectionFactory( h1Config, connectionConfig, requestWriterFactory, responseParserFactory); // 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. final SSLContext sslcontext = SSLContexts.createSystemDefault(); // Create a registry of custom connection socket factories for supported // protocol schemes. final Registry socketFactoryRegistry = RegistryBuilder.create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new SSLConnectionSocketFactory(sslcontext)) .build(); // Use custom DNS resolver to override the system DNS resolution. final 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 a connection manager with custom configuration. final PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager( socketFactoryRegistry, PoolConcurrencyPolicy.STRICT, PoolReusePolicy.LIFO, TimeValue.ofMinutes(5), null, dnsResolver, null); // Configure the connection manager to use socket configuration either // by default or for a specific host. connManager.setDefaultSocketConfig(SocketConfig.custom() .setTcpNoDelay(true) .build()); // Validate connections after 10 sec of inactivity connManager.setDefaultConnectionConfig(ConnectionConfig.custom() .setConnectTimeout(Timeout.ofSeconds(30)) .setSocketTimeout(Timeout.ofSeconds(30)) .setValidateAfterInactivity(TimeValue.ofSeconds(10)) .setTimeToLive(TimeValue.ofHours(1)) .build()); // Use TLS v1.3 only connManager.setDefaultTlsConfig(TlsConfig.custom() .setHandshakeTimeout(Timeout.ofSeconds(30)) .setSupportedProtocols(TLS.V_1_3) .build()); // 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. final CookieStore cookieStore = new BasicCookieStore(); // Use custom credentials provider if necessary. final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .build(); // Create global request configuration final RequestConfig defaultRequestConfig = RequestConfig.custom() .setCookieSpec(StandardCookieSpec.STRICT) .setExpectContinueEnabled(true) .setTargetPreferredAuthSchemes(Arrays.asList(StandardAuthScheme.NTLM, StandardAuthScheme.DIGEST)) .setProxyPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.BASIC)) .build(); // Create an HttpClient with the given custom dependencies and configuration. try (final CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(connManager) .setDefaultCookieStore(cookieStore) .setDefaultCredentialsProvider(credentialsProvider) .setProxy(new HttpHost("myproxy", 8080)) .setDefaultRequestConfig(defaultRequestConfig) .build()) { final 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. final RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig) .setConnectionRequestTimeout(Timeout.ofSeconds(5)) .build(); httpget.setConfig(requestConfig); // Execution context can be customized locally. // Contextual attributes set the local context level will take // precedence over those set at the client level. final HttpClientContext context = ContextBuilder.create() .useCookieStore(cookieStore) .useCredentialsProvider(credentialsProvider) .build(); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); httpclient.execute(httpget, context, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); // Last executed request context.getRequest(); // Execution route context.getHttpRoute(); // Auth exchanges context.getAuthExchanges(); // Cookie origin context.getCookieOrigin(); // Cookie spec used context.getCookieSpec(); // User security token context.getUserToken(); } } } ClientConnectionConfig.java000066400000000000000000000115561434266521000377050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how to use connection configuration on a per-route or a per-host * basis. */ public class ClientConnectionConfig { public final static void main(final String[] args) throws Exception { final PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() .setConnectionConfigResolver(route -> { // Use different settings for all secure (TLS) connections final HttpHost targetHost = route.getTargetHost(); if (route.isSecure()) { return ConnectionConfig.custom() .setConnectTimeout(Timeout.ofMinutes(2)) .setSocketTimeout(Timeout.ofMinutes(2)) .setValidateAfterInactivity(TimeValue.ofMinutes(1)) .setTimeToLive(TimeValue.ofHours(1)) .build(); } else { return ConnectionConfig.custom() .setConnectTimeout(Timeout.ofMinutes(1)) .setSocketTimeout(Timeout.ofMinutes(1)) .setValidateAfterInactivity(TimeValue.ofSeconds(15)) .setTimeToLive(TimeValue.ofMinutes(15)) .build(); } }) .setTlsConfigResolver(host -> { // Use different settings for specific hosts if (host.getSchemeName().equalsIgnoreCase("httpbin.org")) { return TlsConfig.custom() .setSupportedProtocols(TLS.V_1_3) .setHandshakeTimeout(Timeout.ofSeconds(10)) .build(); } else { return TlsConfig.DEFAULT; } }) .build(); try (CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm) .build()) { for (final URIScheme uriScheme : URIScheme.values()) { final ClassicHttpRequest request = ClassicRequestBuilder.get() .setHttpHost(new HttpHost(uriScheme.id, "httpbin.org")) .setPath("/headers") .build(); System.out.println("Executing request " + request); httpclient.execute(request, response -> { System.out.println("----------------------------------------"); System.out.println(request + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } } ClientCustomContext.java000066400000000000000000000063561434266521000373010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.List; import org.apache.hc.client5.http.ContextBuilder; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * This example demonstrates the use of a local HTTP context populated with * custom attributes. */ public class ClientCustomContext { public static void main(final String[] args) throws Exception { try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { // Create a local instance of cookie store final CookieStore cookieStore = new BasicCookieStore(); // Create local HTTP context final HttpClientContext localContext = ContextBuilder.create() // Bind custom cookie store to the local context .useCookieStore(cookieStore) .build(); final HttpGet httpget = new HttpGet("http://httpbin.org/cookies"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); // Pass local context as a parameter httpclient.execute(httpget, localContext, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); final List cookies = cookieStore.getCookies(); for (int i = 0; i < cookies.size(); i++) { System.out.println("Local cookie: " + cookies.get(i)); } EntityUtils.consume(response.getEntity()); return null; }); } } } ClientCustomPublicSuffixList.java000066400000000000000000000104621434266521000411050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.net.URL; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.cookie.RFC6265CookieSpecFactory; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.apache.hc.client5.http.psl.PublicSuffixMatcherLoader; import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.ssl.SSLContexts; /** * This example demonstrates how to use a custom public suffix list. */ public class ClientCustomPublicSuffixList { public static void main(final String[] args) throws Exception { // Use PublicSuffixMatcherLoader to load public suffix list from a file, // resource or from an arbitrary URL final PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load( new URL("https://publicsuffix.org/list/effective_tld_names.dat")); // Please use the publicsuffix.org URL to download the list no more than once per day !!! // Please consider making a local copy !!! final RFC6265CookieSpecFactory cookieSpecFactory = new RFC6265CookieSpecFactory(publicSuffixMatcher); final Lookup cookieSpecRegistry = RegistryBuilder.create() .register(StandardCookieSpec.RELAXED, cookieSpecFactory) .register(StandardCookieSpec.STRICT, cookieSpecFactory) .build(); final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( SSLContexts.createDefault(), new DefaultHostnameVerifier(publicSuffixMatcher)); final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory(sslsf) .build(); try (final CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm) .setDefaultCookieSpecRegistry(cookieSpecRegistry) .build()) { final HttpGet httpget = new HttpGet("https://httpbin.org/get"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } ClientCustomSSL.java000066400000000000000000000105031434266521000363030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.ssl.TLS; import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.util.Timeout; /** * This example demonstrates how to create secure connections with a custom SSL * context. */ public class ClientCustomSSL { public final static void main(final String[] args) throws Exception { // Trust standard CA and those trusted by our custom strategy final SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial((chain, authType) -> { final X509Certificate cert = chain[0]; return "CN=httpbin.org".equalsIgnoreCase(cert.getSubjectDN().getName()); }) .build(); final SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create() .setSslContext(sslcontext) .build(); // Allow TLSv1.3 protocol only final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() .setSSLSocketFactory(sslSocketFactory) .setDefaultTlsConfig(TlsConfig.custom() .setHandshakeTimeout(Timeout.ofSeconds(30)) .setSupportedProtocols(TLS.V_1_3) .build()) .build(); try (CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm) .build()) { final HttpGet httpget = new HttpGet("https://httpbin.org/"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); final HttpClientContext clientContext = HttpClientContext.create(); httpclient.execute(httpget, clientContext, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); final SSLSession sslSession = clientContext.getSSLSession(); if (sslSession != null) { System.out.println("SSL protocol " + sslSession.getProtocol()); System.out.println("SSL cipher suite " + sslSession.getCipherSuite()); } return null; }); } } } ClientEvictExpiredConnections.java000066400000000000000000000067701434266521000412600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.util.TimeValue; /** * Example demonstrating how to evict expired and idle connections * from the connection pool. */ public class ClientEvictExpiredConnections { public static void main(final String[] args) throws Exception { final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(100); try (final CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm) .evictExpiredConnections() .evictIdleConnections(TimeValue.ofSeconds(5)) .build()) { // create an array of URIs to perform GETs on final String[] urisToGet = { "http://hc.apache.org/", "http://hc.apache.org/httpcomponents-core-ga/", "http://hc.apache.org/httpcomponents-client-ga/", }; for (final String requestURI : urisToGet) { final HttpGet request = new HttpGet(requestURI); System.out.println("Executing request " + request.getMethod() + " " + request.getRequestUri()); httpclient.execute(request, response -> { System.out.println("----------------------------------------"); System.out.println(request + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } final PoolStats stats1 = cm.getTotalStats(); System.out.println("Connections kept alive: " + stats1.getAvailable()); // Sleep 10 sec and let the connection evictor do its job Thread.sleep(10000); final PoolStats stats2 = cm.getTotalStats(); System.out.println("Connections kept alive: " + stats2.getAvailable()); } } } ClientExecuteProxy.java000066400000000000000000000052701434266521000371200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * How to send a request via proxy. * * @since 4.0 */ public class ClientExecuteProxy { public static void main(final String[] args)throws Exception { final HttpHost target = new HttpHost("https", "httpbin.org", 443); final HttpHost proxy = new HttpHost("http", "127.0.0.1", 8080); try (final CloseableHttpClient httpclient = HttpClients.custom() .setProxy(proxy) .build()) { final RequestConfig config = RequestConfig.custom() .build(); final HttpGet request = new HttpGet("/get"); request.setConfig(config); System.out.println("Executing request " + request.getMethod() + " " + request.getUri() + " via " + proxy); httpclient.execute(target, request, response -> { System.out.println("----------------------------------------"); System.out.println(request + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } ClientExecuteSOCKS.java000066400000000000000000000113751434266521000366640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; /** * How to send a request via SOCKS proxy. * * @since 4.1 */ public class ClientExecuteSOCKS { public static void main(final String[] args)throws Exception { final Registry reg = RegistryBuilder.create() .register("http", new MyConnectionSocketFactory()) .build(); final InetSocketAddress socksaddr = new InetSocketAddress("mysockshost", 1234); final PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() .setDefaultSocketConfig(SocketConfig.custom() .setSocksProxyAddress(socksaddr) .build()) .build(); try (final CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm) .build()) { final HttpHost target = new HttpHost("http", "httpbin.org", 80); final HttpGet request = new HttpGet("/get"); System.out.println("Executing request " + request.getMethod() + " " + request.getUri() + " via SOCKS proxy " + socksaddr); httpclient.execute(target, request, response -> { System.out.println("----------------------------------------"); System.out.println(request + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } static class MyConnectionSocketFactory implements ConnectionSocketFactory { @Override public Socket createSocket(final HttpContext context) throws IOException { final InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address"); final Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr); return new Socket(proxy); } @Override public Socket connectSocket( final TimeValue connectTimeout, final Socket socket, final HttpHost host, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpContext context) throws IOException { final Socket sock; if (socket != null) { sock = socket; } else { sock = createSocket(context); } if (localAddress != null) { sock.bind(localAddress); } sock.connect(remoteAddress, connectTimeout != null ? connectTimeout.toMillisecondsIntBound() : 0); return sock; } } } ClientFormLogin.java000066400000000000000000000077521434266521000363570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.net.URI; import java.util.List; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; /** * A example that demonstrates how HttpClient APIs can be used to perform * form-based logon. */ public class ClientFormLogin { public static void main(final String[] args) throws Exception { final BasicCookieStore cookieStore = new BasicCookieStore(); try (final CloseableHttpClient httpclient = HttpClients.custom() .setDefaultCookieStore(cookieStore) .build()) { final HttpGet httpget = new HttpGet("https://someportal/"); httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println("Login form get: " + response.getCode() + " " + response.getReasonPhrase()); EntityUtils.consume(response.getEntity()); System.out.println("Initial set of cookies:"); final List cookies = cookieStore.getCookies(); if (cookies.isEmpty()) { System.out.println("None"); } else { for (int i = 0; i < cookies.size(); i++) { System.out.println("- " + cookies.get(i)); } } return null; }); final ClassicHttpRequest login = ClassicRequestBuilder.post() .setUri(new URI("https://someportal/")) .addParameter("IDToken1", "username") .addParameter("IDToken2", "password") .build(); httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println("Login form get: " + response.getCode() + " " + response.getReasonPhrase()); EntityUtils.consume(response.getEntity()); System.out.println("Post logon cookies:"); final List cookies = cookieStore.getCookies(); if (cookies.isEmpty()) { System.out.println("None"); } else { for (int i = 0; i < cookies.size(); i++) { System.out.println("- " + cookies.get(i)); } } return null; }); } } } ClientInterceptors.java000066400000000000000000000107441434266521000371370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.message.StatusLine; import org.apache.hc.core5.http.protocol.HttpContext; /** * This example demonstrates how to insert custom request interceptor and an execution interceptor * to the request execution chain. */ public class ClientInterceptors { public static void main(final String[] args) throws Exception { try (final CloseableHttpClient httpclient = HttpClients.custom() // Add a simple request ID to each outgoing request .addRequestInterceptorFirst(new HttpRequestInterceptor() { private final AtomicLong count = new AtomicLong(0); @Override public void process( final HttpRequest request, final EntityDetails entity, final HttpContext context) throws HttpException, IOException { request.setHeader("request-id", Long.toString(count.incrementAndGet())); } }) // Simulate a 404 response for some requests without passing the message down to the backend .addExecInterceptorAfter(ChainElement.PROTOCOL.name(), "custom", (request, scope, chain) -> { final Header idHeader = request.getFirstHeader("request-id"); if (idHeader != null && "13".equalsIgnoreCase(idHeader.getValue())) { final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NOT_FOUND, "Oppsie"); response.setEntity(new StringEntity("bad luck", ContentType.TEXT_PLAIN)); return response; } else { return chain.proceed(request, scope); } }) .build()) { for (int i = 0; i < 20; i++) { final HttpGet httpget = new HttpGet("http://httpbin.org/get"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } } ClientMultiThreadedExecution.java000066400000000000000000000111541434266521000410710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; /** * An example that performs GETs from multiple threads. * */ public class ClientMultiThreadedExecution { public static void main(final String[] args) throws Exception { // Create an HttpClient with the PoolingHttpClientConnectionManager. // This connection manager must be used if more than one thread will // be using the HttpClient. final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(100); try (final CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm) .build()) { // create an array of URIs to perform GETs on final String[] urisToGet = { "http://hc.apache.org/", "http://hc.apache.org/httpcomponents-core-ga/", "http://hc.apache.org/httpcomponents-client-ga/", }; // create a thread for each URI final GetThread[] threads = new GetThread[urisToGet.length]; for (int i = 0; i < threads.length; i++) { final HttpGet httpget = new HttpGet(urisToGet[i]); threads[i] = new GetThread(httpclient, httpget, i + 1); } // start the threads for (final GetThread thread : threads) { thread.start(); } // join the threads for (final GetThread thread : threads) { thread.join(); } } } /** * A thread that performs a GET. */ static class GetThread extends Thread { private final CloseableHttpClient httpClient; private final HttpContext context; private final HttpGet httpget; private final int id; public GetThread(final CloseableHttpClient httpClient, final HttpGet httpget, final int id) { this.httpClient = httpClient; this.context = new BasicHttpContext(); this.httpget = httpget; this.id = id; } /** * Executes the GetMethod and prints some status information. */ @Override public void run() { try { System.out.println(id + " - about to get something from " + httpget.getUri()); this.httpClient.execute(httpget, response -> { System.out.println(id + " - get executed"); // get the response body as an array of bytes final HttpEntity entity = response.getEntity(); if (entity != null) { final byte[] bytes = EntityUtils.toByteArray(entity); System.out.println(id + " - " + bytes.length + " bytes read"); } return null; }); } catch (final Exception e) { System.out.println(id + " - error: " + e); } } } } ClientMultipartFormPost.java000066400000000000000000000065051434266521000401310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.File; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.entity.mime.FileBody; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; import org.apache.hc.client5.http.entity.mime.StringBody; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * Example how to use multipart/form encoded POST request. */ public class ClientMultipartFormPost { public static void main(final String[] args) throws Exception { if (args.length != 1) { System.out.println("File path not given"); System.exit(1); } try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { final HttpPost httppost = new HttpPost("http://localhost:8080" + "/servlets-examples/servlet/RequestInfoExample"); final FileBody bin = new FileBody(new File(args[0])); final StringBody comment = new StringBody("A binary file of some kind", ContentType.TEXT_PLAIN); final HttpEntity reqEntity = MultipartEntityBuilder.create() .addPart("bin", bin) .addPart("comment", comment) .build(); httppost.setEntity(reqEntity); System.out.println("executing request " + httppost); httpclient.execute(httppost, response -> { System.out.println("----------------------------------------"); System.out.println(httppost + "->" + new StatusLine(response)); final HttpEntity resEntity = response.getEntity(); if (resEntity != null) { System.out.println("Response content length: " + resEntity.getContentLength()); } EntityUtils.consume(response.getEntity()); return null; }); } } } ClientPreemptiveBasicAuthentication.java000066400000000000000000000060711434266521000424360ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.ContextBuilder; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * An example of how HttpClient can be customized to authenticate * preemptively using Basic scheme. * * Generally, preemptive authentication can be considered less * secure than a response to an authentication challenge * and therefore discouraged. */ public class ClientPreemptiveBasicAuthentication { public static void main(final String[] args) throws Exception { try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { final HttpClientContext localContext = ContextBuilder.create() .preemptiveBasicAuth(new HttpHost("http", "httpbin.org"), new UsernamePasswordCredentials("user", "passwd".toCharArray())) .build(); final HttpGet httpget = new HttpGet("http://httpbin.org/hidden-basic-auth/user/passwd"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); for (int i = 0; i < 3; i++) { httpclient.execute(httpget, localContext, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } } ClientPreemptiveDigestAuthentication.java000066400000000000000000000100201434266521000426210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.ContextBuilder; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.auth.DigestScheme; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * An example of how HttpClient can authenticate multiple requests * using the same Digest scheme. After the initial request / response exchange * all subsequent requests sharing the same execution context can re-use * the last Digest nonce value to authenticate with the server. */ public class ClientPreemptiveDigestAuthentication { public static void main(final String[] args) throws Exception { try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { final HttpHost target = new HttpHost("http", "httpbin.org", 80); final HttpClientContext localContext = ContextBuilder.create() .useCredentialsProvider(CredentialsProviderBuilder.create() .add(target, new UsernamePasswordCredentials("user", "passwd".toCharArray())) .build()) .build(); final HttpGet httpget = new HttpGet("http://httpbin.org/digest-auth/auth/user/passwd"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); for (int i = 0; i < 3; i++) { httpclient.execute(httpget, localContext, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); final AuthExchange authExchange = localContext.getAuthExchange(target); if (authExchange != null) { final AuthScheme authScheme = authExchange.getAuthScheme(); if (authScheme instanceof DigestScheme) { final DigestScheme digestScheme = (DigestScheme) authScheme; System.out.println("Nonce: " + digestScheme.getNonce() + "; count: " + digestScheme.getNounceCount()); } } return null; }); } } } } ClientProxyAuthentication.java000066400000000000000000000064421434266521000404770ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * A simple example that uses HttpClient to execute an HTTP request * over a secure connection tunneled through an authenticating proxy. */ public class ClientProxyAuthentication { public static void main(final String[] args) throws Exception { final CredentialsProvider credsProvider = CredentialsProviderBuilder.create() .add(new AuthScope("localhost", 8888), "squid", "squid".toCharArray()) .add(new AuthScope("httpbin.org", 80), "user", "passwd".toCharArray()) .build(); final HttpHost target = new HttpHost("http", "httpbin.org", 80); final HttpHost proxy = new HttpHost("localhost", 8888); try (final CloseableHttpClient httpclient = HttpClients.custom() .setProxy(proxy) .setDefaultCredentialsProvider(credsProvider) .build()) { final RequestConfig config = RequestConfig.custom() .build(); final HttpGet httpget = new HttpGet("/basic-auth/user/passwd"); httpget.setConfig(config); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri() + " via " + proxy); httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); EntityUtils.consume(response.getEntity()); return null; }); } } } ClientResponseProcessing.java000066400000000000000000000052661434266521000403140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.StatusLine; /** * This example demonstrates the recommended way of processing the HTTP response and releasing * associated resources. */ public class ClientResponseProcessing { public static void main(final String[] args) throws Exception { try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { final HttpGet httpget = new HttpGet("http://httpbin.org/get"); System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri()); final Result result = httpclient.execute(httpget, response -> { System.out.println("----------------------------------------"); System.out.println(httpget + "->" + new StatusLine(response)); // Process response message and convert it into a value object return new Result(response.getCode(), EntityUtils.toString(response.getEntity())); }); System.out.println(result); } } static class Result { final int status; final String content; Result(final int status, final String content) { this.status = status; this.content = content; } } } ClientWithRequestFuture.java000066400000000000000000000131241434266521000401300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.FutureRequestExecutionService; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.HttpClientResponseHandler; public class ClientWithRequestFuture { public static void main(final String[] args) throws Exception { // the simplest way to create a HttpAsyncClientWithFuture final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() .setMaxConnPerRoute(5) .setMaxConnTotal(5) .build(); final CloseableHttpClient httpclient = HttpClientBuilder.create() .setConnectionManager(cm) .build(); final ExecutorService execService = Executors.newFixedThreadPool(5); try (final FutureRequestExecutionService requestExecService = new FutureRequestExecutionService( httpclient, execService)) { // Because things are asynchronous, you must provide a HttpClientResponseHandler final HttpClientResponseHandler handler = response -> { // simply return true if the status was OK return response.getCode() == HttpStatus.SC_OK; }; // Simple request ... final HttpGet request1 = new HttpGet("http://httpbin.org/get"); final FutureTask futureTask1 = requestExecService.execute(request1, HttpClientContext.create(), handler); final Boolean wasItOk1 = futureTask1.get(); System.out.println("It was ok? " + wasItOk1); // Cancel a request try { final HttpGet request2 = new HttpGet("http://httpbin.org/get"); final FutureTask futureTask2 = requestExecService.execute(request2, HttpClientContext.create(), handler); futureTask2.cancel(true); final Boolean wasItOk2 = futureTask2.get(); System.out.println("It was cancelled so it should never print this: " + wasItOk2); } catch (final CancellationException e) { System.out.println("We cancelled it, so this is expected"); } // Request with a timeout final HttpGet request3 = new HttpGet("http://httpbin.org/get"); final FutureTask futureTask3 = requestExecService.execute(request3, HttpClientContext.create(), handler); final Boolean wasItOk3 = futureTask3.get(10, TimeUnit.SECONDS); System.out.println("It was ok? " + wasItOk3); final FutureCallback callback = new FutureCallback() { @Override public void completed(final Boolean result) { System.out.println("completed with " + result); } @Override public void failed(final Exception ex) { System.out.println("failed with " + ex.getMessage()); } @Override public void cancelled() { System.out.println("cancelled"); } }; // Simple request with a callback final HttpGet request4 = new HttpGet("http://httpbin.org/get"); // using a null HttpContext here since it is optional // the callback will be called when the task completes, fails, or is cancelled final FutureTask futureTask4 = requestExecService.execute(request4, HttpClientContext.create(), handler, callback); final Boolean wasItOk4 = futureTask4.get(10, TimeUnit.SECONDS); System.out.println("It was ok? " + wasItOk4); } } } ProxyTunnelDemo.java000066400000000000000000000055101434266521000364260ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.Socket; import java.nio.charset.StandardCharsets; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.impl.classic.ProxyClient; import org.apache.hc.core5.http.HttpHost; /** * Example code for using {@link ProxyClient} in order to establish a tunnel through an HTTP proxy. */ public class ProxyTunnelDemo { public final static void main(final String[] args) throws Exception { final ProxyClient proxyClient = new ProxyClient(); final HttpHost target = new HttpHost("www.yahoo.com", 80); final HttpHost proxy = new HttpHost("localhost", 8888); final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("user", "pwd".toCharArray()); try (final Socket socket = proxyClient.tunnel(proxy, target, credentials)) { final Writer out = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.ISO_8859_1); out.write("GET / HTTP/1.1\r\n"); out.write("Host: " + target.toHostString() + "\r\n"); out.write("Agent: whatever\r\n"); out.write("Connection: close\r\n"); out.write("\r\n"); out.flush(); final BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream(), StandardCharsets.ISO_8859_1)); String line = null; while ((line = in.readLine()) != null) { System.out.println(line); } } } } ReactiveClientFullDuplexExchange.java000066400000000000000000000077401434266521000416720ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/examples/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.examples; import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.nio.support.BasicRequestProducer; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactive.ReactiveEntityProducer; import org.apache.hc.core5.reactive.ReactiveResponseConsumer; import org.apache.hc.core5.reactor.IOReactorConfig; import org.reactivestreams.Publisher; import io.reactivex.Flowable; import io.reactivex.Observable; /** * This example demonstrates a reactive, full-duplex HTTP/1.1 message exchange using RxJava. */ public class ReactiveClientFullDuplexExchange { public static void main(final String[] args) throws Exception { final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal( H2Config.DEFAULT, Http1Config.DEFAULT, IOReactorConfig.DEFAULT); client.start(); final URI requestUri = new URI("http://httpbin.org/post"); final byte[] bs = "stuff".getBytes(StandardCharsets.UTF_8); final ReactiveEntityProducer reactiveEntityProducer = new ReactiveEntityProducer( Flowable.just(ByteBuffer.wrap(bs)), bs.length, ContentType.TEXT_PLAIN, null); final BasicRequestProducer requestProducer = new BasicRequestProducer( "POST", requestUri, reactiveEntityProducer); final ReactiveResponseConsumer consumer = new ReactiveResponseConsumer(); final Future requestFuture = client.execute(requestProducer, consumer, null); final Message> streamingResponse = consumer.getResponseFuture().get(); System.out.println(streamingResponse.getHead()); for (final Header header : streamingResponse.getHead().getHeaders()) { System.out.println(header); } System.out.println(); Observable.fromPublisher(streamingResponse.getBody()) .map(byteBuffer -> { final byte[] string = new byte[byteBuffer.remaining()]; byteBuffer.get(string); return new String(string); }) .materialize() .forEach(System.out::println); requestFuture.get(1, TimeUnit.MINUTES); System.out.println("Shutting down"); client.close(CloseMode.GRACEFUL); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/000077500000000000000000000000001434266521000316505ustar00rootroot00000000000000ExecSupportTest.java000066400000000000000000000031631434266521000355600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class ExecSupportTest { @Test public void testGetNextExchangeId() { final long base = ExecSupport.getNextExecNumber(); for (int i = 1; i <= 1_000_000; i++) { Assertions.assertEquals( String.format("ex-%010d", i + base), ExecSupport.getNextExchangeId()); } } } PrefixedIncrementingIdTest.java000066400000000000000000000042551434266521000376700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class PrefixedIncrementingIdTest { @Test void testGetNextId() { final PrefixedIncrementingId testId = new PrefixedIncrementingId("test-"); Assertions.assertEquals(String.format("test-%010d", 1), testId.getNextId()); Assertions.assertEquals(String.format("test-%010d", 2), testId.getNextId()); } @Test public void testCreateId() { final PrefixedIncrementingId exchangeId = new PrefixedIncrementingId("ex-"); Assertions.assertEquals(String.format("ex-%010d", 0), exchangeId.createId(0)); for (long i = 1; i <= 100_000_000L; i *= 10) { Assertions.assertEquals(String.format("ex-%010d", i - 1), exchangeId.createId(i - 1)); Assertions.assertEquals(String.format("ex-%010d", i), exchangeId.createId(i)); Assertions.assertEquals(String.format("ex-%010d", i + 1), exchangeId.createId(i + 1)); } } } TestAuthenticationStrategy.java000066400000000000000000000173221434266521000400030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.auth.DigestScheme; import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.config.Registry; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link DefaultAuthenticationStrategy}. */ @SuppressWarnings("boxing") // test code public class TestAuthenticationStrategy { @Test public void testSelectInvalidInput() throws Exception { final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpClientContext context = HttpClientContext.create(); Assertions.assertThrows(NullPointerException.class, () -> authStrategy.select(null, Collections.emptyMap(), context)); Assertions.assertThrows(NullPointerException.class, () -> authStrategy.select(ChallengeType.TARGET, null, context)); Assertions.assertThrows(NullPointerException.class, () -> authStrategy.select(ChallengeType.TARGET, Collections.emptyMap(), null)); } @Test public void testSelectNoSchemeRegistry() throws Exception { final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpClientContext context = HttpClientContext.create(); final Map challenges = new HashMap<>(); challenges.put(StandardAuthScheme.BASIC.toLowerCase(Locale.ROOT), new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.BASIC, new BasicNameValuePair("realm", "test"))); challenges.put(StandardAuthScheme.DIGEST.toLowerCase(Locale.ROOT), new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.DIGEST, new BasicNameValuePair("realm", "test"), new BasicNameValuePair("nonce", "1234"))); final List authSchemes = authStrategy.select(ChallengeType.TARGET, challenges, context); Assertions.assertNotNull(authSchemes); Assertions.assertEquals(0, authSchemes.size()); } @Test public void testUnsupportedScheme() throws Exception { final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpClientContext context = HttpClientContext.create(); final Map challenges = new HashMap<>(); challenges.put(StandardAuthScheme.BASIC.toLowerCase(Locale.ROOT), new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.BASIC, new BasicNameValuePair("realm", "realm1"))); challenges.put(StandardAuthScheme.DIGEST.toLowerCase(Locale.ROOT), new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.DIGEST, new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234"))); challenges.put("whatever", new AuthChallenge(ChallengeType.TARGET, "Whatever", new BasicNameValuePair("realm", "realm3"))); final Registry authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE).build(); context.setAuthSchemeRegistry(authSchemeRegistry); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(new AuthScope("somehost", 80), "user", "pwd".toCharArray()) .build()); final List authSchemes = authStrategy.select(ChallengeType.TARGET, challenges, context); Assertions.assertNotNull(authSchemes); Assertions.assertEquals(2, authSchemes.size()); final AuthScheme authScheme1 = authSchemes.get(0); Assertions.assertTrue(authScheme1 instanceof DigestScheme); final AuthScheme authScheme2 = authSchemes.get(1); Assertions.assertTrue(authScheme2 instanceof BasicScheme); } @Test public void testCustomAuthPreference() throws Exception { final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final RequestConfig config = RequestConfig.custom() .setTargetPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.BASIC)) .build(); final HttpClientContext context = HttpClientContext.create(); final Map challenges = new HashMap<>(); challenges.put(StandardAuthScheme.BASIC.toLowerCase(Locale.ROOT), new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.BASIC, new BasicNameValuePair("realm", "realm1"))); challenges.put(StandardAuthScheme.DIGEST.toLowerCase(Locale.ROOT), new AuthChallenge(ChallengeType.TARGET, StandardAuthScheme.DIGEST, new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234"))); final Registry authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE).build(); context.setAuthSchemeRegistry(authSchemeRegistry); context.setRequestConfig(config); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(new AuthScope("somehost", 80), "user", "pwd".toCharArray()) .build()); final List authSchemes = authStrategy.select(ChallengeType.TARGET, challenges, context); Assertions.assertNotNull(authSchemes); Assertions.assertEquals(1, authSchemes.size()); final AuthScheme authScheme1 = authSchemes.get(0); Assertions.assertTrue(authScheme1 instanceof BasicScheme); } } TestDefaultConnKeepAliveStrategy.java000066400000000000000000000116271434266521000410160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link DefaultConnectionKeepAliveStrategy}. */ public class TestDefaultConnKeepAliveStrategy { @Test public void testIllegalResponseArg() throws Exception { final HttpContext context = new BasicHttpContext(null); final ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy(); Assertions.assertThrows(NullPointerException.class, () -> keepAliveStrat.getKeepAliveDuration(null, context)); } @Test public void testNoKeepAliveHeader() throws Exception { final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig( RequestConfig.custom() .setConnectionKeepAlive(TimeValue.NEG_ONE_MILLISECOND) .build()); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); final ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy(); final TimeValue d = keepAliveStrat.getKeepAliveDuration(response, context); Assertions.assertEquals(TimeValue.NEG_ONE_MILLISECOND, d); } @Test public void testEmptyKeepAliveHeader() throws Exception { final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig( RequestConfig.custom() .setConnectionKeepAlive(TimeValue.NEG_ONE_MILLISECOND) .build()); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.addHeader("Keep-Alive", "timeout, max=20"); final ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy(); final TimeValue d = keepAliveStrat.getKeepAliveDuration(response, context); Assertions.assertEquals(TimeValue.NEG_ONE_MILLISECOND, d); } @Test public void testInvalidKeepAliveHeader() throws Exception { final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig( RequestConfig.custom() .setConnectionKeepAlive(TimeValue.NEG_ONE_MILLISECOND) .build()); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.addHeader("Keep-Alive", "timeout=whatever, max=20"); final ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy(); final TimeValue d = keepAliveStrat.getKeepAliveDuration(response, context); Assertions.assertEquals(TimeValue.NEG_ONE_MILLISECOND, d); } @Test public void testKeepAliveHeader() throws Exception { final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig( RequestConfig.custom() .setConnectionKeepAlive(TimeValue.NEG_ONE_MILLISECOND) .build()); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK); response.addHeader("Keep-Alive", "timeout=300, max=20"); final ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy(); final TimeValue d = keepAliveStrat.getKeepAliveDuration(response, context); Assertions.assertEquals(TimeValue.ofSeconds(300), d); } } TestDefaultHttpRequestRetryStrategy.java000066400000000000000000000154151434266521000416500ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.io.IOException; import java.net.ConnectException; import java.net.NoRouteToHostException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.time.Instant; import java.time.temporal.ChronoUnit; import javax.net.ssl.SSLException; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestDefaultHttpRequestRetryStrategy { private DefaultHttpRequestRetryStrategy retryStrategy; @BeforeEach public void setup() { this.retryStrategy = new DefaultHttpRequestRetryStrategy(3, TimeValue.ofMilliseconds(1234L)); } @Test public void testBasics() throws Exception { final HttpResponse response1 = new BasicHttpResponse(503, "Oopsie"); Assertions.assertTrue(this.retryStrategy.retryRequest(response1, 1, null)); Assertions.assertTrue(this.retryStrategy.retryRequest(response1, 2, null)); Assertions.assertTrue(this.retryStrategy.retryRequest(response1, 3, null)); Assertions.assertFalse(this.retryStrategy.retryRequest(response1, 4, null)); final HttpResponse response2 = new BasicHttpResponse(500, "Big Time Oopsie"); Assertions.assertFalse(this.retryStrategy.retryRequest(response2, 1, null)); final HttpResponse response3 = new BasicHttpResponse(429, "Oopsie"); Assertions.assertTrue(this.retryStrategy.retryRequest(response3, 1, null)); Assertions.assertTrue(this.retryStrategy.retryRequest(response3, 2, null)); Assertions.assertTrue(this.retryStrategy.retryRequest(response3, 3, null)); Assertions.assertFalse(this.retryStrategy.retryRequest(response3, 4, null)); Assertions.assertEquals(TimeValue.ofMilliseconds(1234L), this.retryStrategy.getRetryInterval(response1, 1, null)); } @Test public void testRetryAfterHeaderAsLong() throws Exception { final HttpResponse response = new BasicHttpResponse(503, "Oopsie"); response.setHeader(HttpHeaders.RETRY_AFTER, "321"); Assertions.assertEquals(TimeValue.ofSeconds(321L), this.retryStrategy.getRetryInterval(response, 3, null)); } @Test public void testRetryAfterHeaderAsDate() throws Exception { this.retryStrategy = new DefaultHttpRequestRetryStrategy(3, TimeValue.ZERO_MILLISECONDS); final HttpResponse response = new BasicHttpResponse(503, "Oopsie"); response.setHeader(HttpHeaders.RETRY_AFTER, DateUtils.formatStandardDate(Instant.now().plus(100, ChronoUnit.SECONDS))); Assertions.assertTrue(this.retryStrategy.getRetryInterval(response, 3, null).compareTo(TimeValue.ZERO_MILLISECONDS) > 0); } @Test public void testRetryAfterHeaderAsPastDate() throws Exception { final HttpResponse response = new BasicHttpResponse(503, "Oopsie"); response.setHeader(HttpHeaders.RETRY_AFTER, DateUtils.formatStandardDate(Instant.now().minus(100, ChronoUnit.SECONDS))); Assertions.assertEquals(TimeValue.ofMilliseconds(1234L), this.retryStrategy.getRetryInterval(response, 3, null)); } @Test public void testInvalidRetryAfterHeader() throws Exception { final HttpResponse response = new BasicHttpResponse(503, "Oopsie"); response.setHeader(HttpHeaders.RETRY_AFTER, "Stuff"); Assertions.assertEquals(TimeValue.ofMilliseconds(1234L), retryStrategy.getRetryInterval(response, 3, null)); } @Test public void noRetryOnConnectTimeout() throws Exception { final HttpGet request = new HttpGet("/"); Assertions.assertFalse(retryStrategy.retryRequest(request, new SocketTimeoutException(), 1, null)); } @Test public void noRetryOnConnect() throws Exception { final HttpGet request = new HttpGet("/"); Assertions.assertFalse(retryStrategy.retryRequest(request, new ConnectException(), 1, null)); } @Test public void noRetryOnConnectionClosed() throws Exception { final HttpGet request = new HttpGet("/"); Assertions.assertFalse(retryStrategy.retryRequest(request, new ConnectionClosedException(), 1, null)); } @Test public void noRetryForNoRouteToHostException() { final HttpGet request = new HttpGet("/"); Assertions.assertFalse(retryStrategy.retryRequest(request, new NoRouteToHostException(), 1, null)); } @Test public void noRetryOnSSLFailure() throws Exception { final HttpGet request = new HttpGet("/"); Assertions.assertFalse(retryStrategy.retryRequest(request, new SSLException("encryption failed"), 1, null)); } @Test public void noRetryOnUnknownHost() throws Exception { final HttpGet request = new HttpGet("/"); Assertions.assertFalse(retryStrategy.retryRequest(request, new UnknownHostException(), 1, null)); } @Test public void noRetryOnAbortedRequests() throws Exception { final HttpGet request = new HttpGet("/"); request.cancel(); Assertions.assertFalse(retryStrategy.retryRequest(request, new IOException(), 1, null)); } @Test public void retryOnNonAbortedRequests() throws Exception { final HttpGet request = new HttpGet("/"); Assertions.assertTrue(retryStrategy.retryRequest(request, new IOException(), 1, null)); } } TestDefaultRedirectStrategy.java000066400000000000000000000277641434266521000401050ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import java.net.URI; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestDefaultRedirectStrategy { @Test public void testIsRedirectedMovedTemporary() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); Assertions.assertFalse(redirectStrategy.isRedirected(httpget, response, context)); response.setHeader(HttpHeaders.LOCATION, "http://localhost/blah"); Assertions.assertTrue(redirectStrategy.isRedirected(httpget, response, context)); } @Test public void testIsRedirectedMovedTemporaryNoLocation() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); Assertions.assertFalse(redirectStrategy.isRedirected(httpget, response, context)); } @Test public void testIsRedirectedMovedPermanently() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_PERMANENTLY, "Redirect"); Assertions.assertFalse(redirectStrategy.isRedirected(httpget, response, context)); response.setHeader(HttpHeaders.LOCATION, "http://localhost/blah"); Assertions.assertTrue(redirectStrategy.isRedirected(httpget, response, context)); } @Test public void testIsRedirectedTemporaryRedirect() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_TEMPORARY_REDIRECT, "Redirect"); Assertions.assertFalse(redirectStrategy.isRedirected(httpget, response, context)); response.setHeader(HttpHeaders.LOCATION, "http://localhost/blah"); Assertions.assertTrue(redirectStrategy.isRedirected(httpget, response, context)); } @Test public void testIsRedirectedSeeOther() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SEE_OTHER, "Redirect"); Assertions.assertFalse(redirectStrategy.isRedirected(httpget, response, context)); response.setHeader(HttpHeaders.LOCATION, "http://localhost/blah"); Assertions.assertTrue(redirectStrategy.isRedirected(httpget, response, context)); } @Test public void testIsRedirectedUnknownStatus() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(333, "Redirect"); Assertions.assertFalse(redirectStrategy.isRedirected(httpget, response, context)); final HttpPost httppost = new HttpPost("http://localhost/"); Assertions.assertFalse(redirectStrategy.isRedirected(httppost, response, context)); } @Test public void testIsRedirectedInvalidInput() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SEE_OTHER, "Redirect"); Assertions.assertThrows(NullPointerException.class, () -> redirectStrategy.isRedirected(null, response, context)); Assertions.assertThrows(NullPointerException.class, () -> redirectStrategy.isRedirected(httpget, null, context)); } @Test public void testGetLocationUri() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); response.addHeader("Location", "http://localhost/stuff"); final URI uri = redirectStrategy.getLocationURI(httpget, response, context); Assertions.assertEquals(URI.create("http://localhost/stuff"), uri); } @Test public void testGetLocationUriMissingHeader() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); Assertions.assertThrows(HttpException.class, () -> redirectStrategy.getLocationURI(httpget, response, context)); } @Test public void testGetLocationUriInvalidLocation() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); response.addHeader("Location", "http://localhost/not valid"); Assertions.assertThrows(ProtocolException.class, () -> redirectStrategy.getLocationURI(httpget, response, context)); } @Test public void testGetLocationUriRelative() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); response.addHeader("Location", "/stuff"); final URI uri = redirectStrategy.getLocationURI(httpget, response, context); Assertions.assertEquals(URI.create("http://localhost/stuff"), uri); } @Test public void testGetLocationUriRelativeWithFragment() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); response.addHeader("Location", "/stuff#fragment"); final URI uri = redirectStrategy.getLocationURI(httpget, response, context); Assertions.assertEquals(URI.create("http://localhost/stuff#fragment"), uri); } @Test public void testGetLocationUriAbsoluteWithFragment() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); response.addHeader("Location", "http://localhost/stuff#fragment"); final URI uri = redirectStrategy.getLocationURI(httpget, response, context); Assertions.assertEquals(URI.create("http://localhost/stuff#fragment"), uri); } @Test public void testGetLocationUriNormalized() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); response.addHeader("Location", "http://localhost/././stuff/../morestuff"); final URI uri = redirectStrategy.getLocationURI(httpget, response, context); Assertions.assertEquals(URI.create("http://localhost/morestuff"), uri); } @Test public void testGetLocationUriInvalidInput() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); final HttpClientContext context = HttpClientContext.create(); final HttpGet httpget = new HttpGet("http://localhost/"); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY, "Redirect"); response.addHeader("Location", "http://localhost/stuff"); Assertions.assertThrows(NullPointerException.class, () -> redirectStrategy.getLocationURI(null, response, context)); Assertions.assertThrows(NullPointerException.class, () -> redirectStrategy.getLocationURI(httpget, null, context)); Assertions.assertThrows(NullPointerException.class, () -> redirectStrategy.getLocationURI(httpget, response, null)); } @Test public void testCreateLocationURI() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); Assertions.assertEquals("http://blahblah/", redirectStrategy.createLocationURI("http://BlahBlah").toASCIIString()); } @Test public void testCreateLocationURIInvalid() throws Exception { final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); Assertions.assertThrows(ProtocolException.class, () -> redirectStrategy.createLocationURI(":::::::")); } } TestIdleConnectionEvictor.java000066400000000000000000000060131434266521000375250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; /** * Unit tests for {@link IdleConnectionEvictor}. */ public class TestIdleConnectionEvictor { @Test public void testEvictExpiredAndIdle() throws Exception { final ConnPoolControl cm = Mockito.mock(ConnPoolControl.class); final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm, TimeValue.ofMilliseconds(500), TimeValue.ofSeconds(3)); connectionEvictor.start(); Thread.sleep(1000); Mockito.verify(cm, Mockito.atLeast(1)).closeExpired(); Mockito.verify(cm, Mockito.atLeast(1)).closeIdle(TimeValue.ofSeconds(3)); Assertions.assertTrue(connectionEvictor.isRunning()); connectionEvictor.shutdown(); connectionEvictor.awaitTermination(Timeout.ofSeconds(1)); Assertions.assertFalse(connectionEvictor.isRunning()); } @Test public void testEvictExpiredOnly() throws Exception { final ConnPoolControl cm = Mockito.mock(ConnPoolControl.class); final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(cm, TimeValue.ofMilliseconds(500), null); connectionEvictor.start(); Thread.sleep(1000); Mockito.verify(cm, Mockito.atLeast(1)).closeExpired(); Mockito.verify(cm, Mockito.never()).closeIdle(ArgumentMatchers.any()); Assertions.assertTrue(connectionEvictor.isRunning()); connectionEvictor.shutdown(); connectionEvictor.awaitTermination(Timeout.ofSeconds(1)); Assertions.assertFalse(connectionEvictor.isRunning()); } } TestRequestSupport.java000066400000000000000000000054161434266521000363270ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link RequestSupport}. */ public class TestRequestSupport { @Test public void testPathPrefixExtraction() { Assertions.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb"))); Assertions.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/"))); Assertions.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/../aaaa/"))); Assertions.assertEquals("/aaaa/bbbb/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb/cccc"))); Assertions.assertEquals("/aaaa/bbbb/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb/"))); Assertions.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb?////"))); Assertions.assertEquals("/aa%2Faa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aa%2faa/bbbb"))); Assertions.assertEquals("/aa%2Faa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/a%61%2fa%61/bbbb"))); Assertions.assertEquals("/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/"))); Assertions.assertEquals("/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa"))); Assertions.assertEquals("/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", ""))); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/000077500000000000000000000000001434266521000326115ustar00rootroot00000000000000TestAuthChallengeParser.java000066400000000000000000000432101434266521000401160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import static org.hamcrest.MatcherAssert.assertThat; import java.util.List; import org.apache.hc.client5.http.NameValuePairMatcher; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.util.CharArrayBuffer; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestAuthChallengeParser { private AuthChallengeParser parser; @BeforeEach public void setUp() throws Exception { this.parser = new AuthChallengeParser(); } @Test public void testParseTokenTerminatedByBlank() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("aaabbbbccc "); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc")); } @Test public void testParseTokenTerminatedByComma() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("aaabbbbccc, "); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc")); } @Test public void testParseTokenTerminatedByEndOfStream() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("aaabbbbccc"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc")); } @Test public void testParsePaddedToken68() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("aaabbbbccc==== "); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc====")); assertThat(cursor.atEnd(), CoreMatchers.equalTo(false)); assertThat(buffer.charAt(cursor.getPos()), CoreMatchers.equalTo(' ')); } @Test public void testParsePaddedToken68SingleEqual() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("aaabbbbccc="); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc=")); assertThat(cursor.atEnd(), CoreMatchers.equalTo(true)); } @Test public void testParsePaddedToken68MultipleEquals() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(16); buffer.append("aaabbbbccc======"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc======")); assertThat(cursor.atEnd(), CoreMatchers.equalTo(true)); } @Test public void testParsePaddedToken68TerminatedByComma() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("aaabbbbccc====,"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc====")); assertThat(cursor.atEnd(), CoreMatchers.equalTo(false)); assertThat(buffer.charAt(cursor.getPos()), CoreMatchers.equalTo(',')); } @Test public void testParseTokenTerminatedByParameter() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("aaabbbbccc=blah"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); assertThat(parser.parseToken(buffer, cursor), CoreMatchers.equalTo("aaabbbbccc")); assertThat(cursor.atEnd(), CoreMatchers.equalTo(false)); assertThat(buffer.charAt(cursor.getPos()), CoreMatchers.equalTo('=')); } @Test public void testParseBasicAuthChallenge() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append(StandardAuthScheme.BASIC + " realm=blah"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(1, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals(StandardAuthScheme.BASIC, challenge1.getSchemeName()); Assertions.assertNull(challenge1.getValue()); final List params = challenge1.getParams(); Assertions.assertNotNull(params); Assertions.assertEquals(1, params.size()); assertThat(params.get(0), NameValuePairMatcher.equals("realm", "blah")); } @Test public void testParseAuthChallengeWithBlanks() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append(" " + StandardAuthScheme.BASIC + " realm = blah "); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(1, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals(StandardAuthScheme.BASIC, challenge1.getSchemeName()); Assertions.assertNull(challenge1.getValue()); final List params = challenge1.getParams(); Assertions.assertNotNull(params); Assertions.assertEquals(1, params.size()); assertThat(params.get(0), NameValuePairMatcher.equals("realm", "blah")); } @Test public void testParseMultipleAuthChallenge() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("This xxxxxxxxxxxxxxxxxxxxxx, " + "That yyyyyyyyyyyyyyyyyyyyyy "); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(2, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals("This", challenge1.getSchemeName()); Assertions.assertEquals("xxxxxxxxxxxxxxxxxxxxxx", challenge1.getValue()); Assertions.assertNull(challenge1.getParams()); final AuthChallenge challenge2 = challenges.get(1); Assertions.assertEquals("That", challenge2.getSchemeName()); Assertions.assertEquals("yyyyyyyyyyyyyyyyyyyyyy", challenge2.getValue()); Assertions.assertNull(challenge2.getParams()); } @Test public void testParseMultipleAuthChallengeWithParams() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append(StandardAuthScheme.BASIC + " realm=blah, param1 = this, param2=that, " + StandardAuthScheme.BASIC + " realm=\"\\\"yada\\\"\", this, that=,this-and-that "); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(2, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals(StandardAuthScheme.BASIC, challenge1.getSchemeName()); Assertions.assertNull(challenge1.getValue()); final List params1 = challenge1.getParams(); Assertions.assertNotNull(params1); Assertions.assertEquals(3, params1.size()); assertThat(params1.get(0), NameValuePairMatcher.equals("realm", "blah")); assertThat(params1.get(1), NameValuePairMatcher.equals("param1", "this")); assertThat(params1.get(2), NameValuePairMatcher.equals("param2", "that")); final AuthChallenge challenge2 = challenges.get(1); Assertions.assertEquals(StandardAuthScheme.BASIC, challenge2.getSchemeName()); Assertions.assertNull(challenge2.getValue()); final List params2 = challenge2.getParams(); Assertions.assertNotNull(params2); Assertions.assertEquals(4, params2.size()); assertThat(params2.get(0), NameValuePairMatcher.equals("realm", "\"yada\"")); assertThat(params2.get(1), NameValuePairMatcher.equals("this", null)); assertThat(params2.get(2), NameValuePairMatcher.equals("that", "")); assertThat(params2.get(3), NameValuePairMatcher.equals("this-and-that", null)); } @Test public void testParseMultipleAuthChallengeWithParamsContainingComma() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append(StandardAuthScheme.BASIC + " realm=blah, param1 = \"this, param2=that\", " + StandardAuthScheme.BASIC + " realm=\"\\\"yada,,,,\\\"\""); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(2, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals(StandardAuthScheme.BASIC, challenge1.getSchemeName()); Assertions.assertNull(challenge1.getValue()); final List params1 = challenge1.getParams(); Assertions.assertNotNull(params1); Assertions.assertEquals(2, params1.size()); assertThat(params1.get(0), NameValuePairMatcher.equals("realm", "blah")); assertThat(params1.get(1), NameValuePairMatcher.equals("param1", "this, param2=that")); final AuthChallenge challenge2 = challenges.get(1); Assertions.assertEquals(StandardAuthScheme.BASIC, challenge2.getSchemeName()); Assertions.assertNull(challenge2.getValue()); final List params2 = challenge2.getParams(); Assertions.assertNotNull(params2); Assertions.assertEquals(1, params2.size()); assertThat(params2.get(0), NameValuePairMatcher.equals("realm", "\"yada,,,,\"")); } @Test public void testParseEmptyAuthChallenge1() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("This"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(1, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals("This", challenge1.getSchemeName()); Assertions.assertNull(challenge1.getValue()); Assertions.assertNull(challenge1.getParams()); } @Test public void testParseMalformedAuthChallenge1() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("This , "); final ParserCursor cursor = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parse(ChallengeType.TARGET, buffer, cursor)); } @Test public void testParseMalformedAuthChallenge2() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("This = that"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parse(ChallengeType.TARGET, buffer, cursor)); } @Test public void testParseMalformedAuthChallenge3() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("blah blah blah"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); Assertions.assertThrows(ParseException.class, () -> parser.parse(ChallengeType.TARGET, buffer, cursor)); } @Test public void testParseValidAuthChallenge1() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("blah blah"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(1, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals("blah", challenge1.getSchemeName()); Assertions.assertEquals("blah", challenge1.getValue()); Assertions.assertNull(challenge1.getParams()); } @Test public void testParseValidAuthChallenge2() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("blah blah, blah"); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(1, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals("blah", challenge1.getSchemeName()); Assertions.assertNull(challenge1.getValue()); final List params1 = challenge1.getParams(); Assertions.assertNotNull(params1); Assertions.assertEquals(2, params1.size()); assertThat(params1.get(0), NameValuePairMatcher.equals("blah", null)); assertThat(params1.get(1), NameValuePairMatcher.equals("blah", null)); } @Test public void testParseEmptyNTLMAuthChallenge() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append(StandardAuthScheme.NTLM); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(1, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); Assertions.assertEquals(StandardAuthScheme.NTLM, challenge1.getSchemeName()); Assertions.assertNull(challenge1.getValue()); } @Test public void testParseParameterAndToken68AuthChallengeMix() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); buffer.append("scheme1 aaaa , scheme2 aaaa==, scheme3 aaaa=aaaa, scheme4 aaaa="); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List challenges = parser.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertNotNull(challenges); Assertions.assertEquals(4, challenges.size()); final AuthChallenge challenge1 = challenges.get(0); assertThat(challenge1.getSchemeName(), CoreMatchers.equalTo("scheme1")); assertThat(challenge1.getValue(), CoreMatchers.equalTo("aaaa")); assertThat(challenge1.getParams(), CoreMatchers.nullValue()); final AuthChallenge challenge2 = challenges.get(1); assertThat(challenge2.getSchemeName(), CoreMatchers.equalTo("scheme2")); assertThat(challenge2.getValue(), CoreMatchers.equalTo("aaaa==")); assertThat(challenge2.getParams(), CoreMatchers.nullValue()); final AuthChallenge challenge3 = challenges.get(2); assertThat(challenge3.getSchemeName(), CoreMatchers.equalTo("scheme3")); assertThat(challenge3.getValue(), CoreMatchers.nullValue()); assertThat(challenge3.getParams(), CoreMatchers.notNullValue()); assertThat(challenge3.getParams().size(), CoreMatchers.equalTo(1)); assertThat(challenge3.getParams().get(0), NameValuePairMatcher.equals("aaaa", "aaaa")); final AuthChallenge challenge4 = challenges.get(3); assertThat(challenge4.getSchemeName(), CoreMatchers.equalTo("scheme4")); assertThat(challenge4.getValue(), CoreMatchers.equalTo("aaaa=")); assertThat(challenge4.getParams(), CoreMatchers.nullValue()); } } TestBasicAuthCache.java000066400000000000000000000057241434266521000370340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link BasicAuthCache}. */ @SuppressWarnings("boxing") // test code public class TestBasicAuthCache { @Test public void testBasicStoreRestore() throws Exception { final BasicAuthCache cache = new BasicAuthCache(); final AuthScheme authScheme = new BasicScheme(); cache.put(new HttpHost("localhost", 80), authScheme); Assertions.assertNotNull(cache.get(new HttpHost("localhost", 80))); cache.remove(new HttpHost("localhost", 80)); Assertions.assertNull(cache.get(new HttpHost("localhost", 80))); cache.put(new HttpHost("localhost", 80), authScheme); cache.clear(); Assertions.assertNull(cache.get(new HttpHost("localhost", 80))); } @Test public void testNullKey() throws Exception { final BasicAuthCache cache = new BasicAuthCache(); final AuthScheme authScheme = new BasicScheme(); Assertions.assertThrows(NullPointerException.class, () -> cache.put(null, authScheme)); } @Test public void testNullAuthScheme() throws Exception { final BasicAuthCache cache = new BasicAuthCache(); cache.put(new HttpHost("localhost", 80), null); Assertions.assertNull(cache.get(new HttpHost("localhost", 80))); } @Test public void testStoreNonSerializable() throws Exception { final BasicAuthCache cache = new BasicAuthCache(); final AuthScheme authScheme = new NTLMScheme(); cache.put(new HttpHost("localhost", 80), authScheme); Assertions.assertNull(cache.get(new HttpHost("localhost", 80))); } } TestBasicScheme.java000066400000000000000000000257031434266521000364120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Basic authentication test cases. */ public class TestBasicScheme { private static final Base64.Encoder BASE64_ENC = Base64.getEncoder(); private static AuthChallenge parse(final String s) throws ParseException { final CharArrayBuffer buffer = new CharArrayBuffer(s.length()); buffer.append(s); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List authChallenges = AuthChallengeParser.INSTANCE.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertEquals(1, authChallenges.size()); return authChallenges.get(0); } @Test public void testBasicAuthenticationEmptyChallenge() throws Exception { final String challenge = StandardAuthScheme.BASIC; final AuthChallenge authChallenge = parse(challenge); final AuthScheme authscheme = new BasicScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertNull(authscheme.getRealm()); } @Test public void testBasicAuthentication() throws Exception { final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final BasicScheme authscheme = new BasicScheme(); authscheme.processChallenge(authChallenge, null); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "test", null), "testuser", "testpass".toCharArray()) .build(); final HttpRequest request = new BasicHttpRequest("GET", "/"); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); final byte[] testCreds = "testuser:testpass".getBytes(StandardCharsets.US_ASCII); final String expected = "Basic " + BASE64_ENC.encodeToString(testCreds); Assertions.assertEquals(expected, authResponse); Assertions.assertEquals("test", authscheme.getRealm()); Assertions.assertTrue(authscheme.isChallengeComplete()); Assertions.assertFalse(authscheme.isConnectionBased()); } static final String TEST_UTF8_PASSWORD = "123\u00A3"; @Test public void testBasicAuthenticationDefaultCharsetASCII() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("test", TEST_UTF8_PASSWORD.toCharArray()); final BasicScheme authscheme = new BasicScheme(StandardCharsets.US_ASCII); final HttpRequest request = new BasicHttpRequest("GET", "/"); authscheme.initPreemptive(creds); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertEquals("Basic dGVzdDoxMjM/", authResponse); } @Test public void testBasicAuthenticationDefaultCharsetISO88591() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("test", TEST_UTF8_PASSWORD.toCharArray()); final BasicScheme authscheme = new BasicScheme(StandardCharsets.ISO_8859_1); final HttpRequest request = new BasicHttpRequest("GET", "/"); authscheme.initPreemptive(creds); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertEquals("Basic dGVzdDoxMjOj", authResponse); } @Test public void testBasicAuthenticationDefaultCharsetUTF8() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("test", TEST_UTF8_PASSWORD.toCharArray()); final BasicScheme authscheme = new BasicScheme(StandardCharsets.UTF_8); final HttpRequest request = new BasicHttpRequest("GET", "/"); authscheme.initPreemptive(creds); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertEquals("Basic dGVzdDoxMjPCow==", authResponse); } @Test public void testBasicAuthenticationWithCharset() throws Exception { final AuthChallenge authChallenge = parse("Basic realm=\"test\", charset=\"utf-8\""); final BasicScheme authscheme = new BasicScheme(); authscheme.processChallenge(authChallenge, null); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "test", null), "test", TEST_UTF8_PASSWORD.toCharArray()) .build(); final HttpRequest request = new BasicHttpRequest("GET", "/"); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertEquals("Basic dGVzdDoxMjPCow==", authResponse); Assertions.assertEquals("test", authscheme.getRealm()); Assertions.assertTrue(authscheme.isChallengeComplete()); Assertions.assertFalse(authscheme.isConnectionBased()); } @Test public void testSerialization() throws Exception { final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final BasicScheme basicScheme = new BasicScheme(); basicScheme.processChallenge(authChallenge, null); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(buffer); out.writeObject(basicScheme); out.flush(); final byte[] raw = buffer.toByteArray(); final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw)); final BasicScheme authScheme = (BasicScheme) in.readObject(); Assertions.assertEquals(basicScheme.getName(), authScheme.getName()); Assertions.assertEquals(basicScheme.getRealm(), authScheme.getRealm()); Assertions.assertEquals(basicScheme.isChallengeComplete(), authScheme.isChallengeComplete()); } @Test public void testSerializationUnchallenged() throws Exception { final BasicScheme basicScheme = new BasicScheme(); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(buffer); out.writeObject(basicScheme); out.flush(); final byte[] raw = buffer.toByteArray(); final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw)); final BasicScheme authScheme = (BasicScheme) in.readObject(); Assertions.assertEquals(basicScheme.getName(), authScheme.getName()); Assertions.assertEquals(basicScheme.getRealm(), authScheme.getRealm()); Assertions.assertEquals(basicScheme.isChallengeComplete(), authScheme.isChallengeComplete()); } @Test public void testBasicAuthenticationUserCredentialsMissing() throws Exception { final BasicScheme authscheme = new BasicScheme(); final HttpHost host = new HttpHost("somehost", 80); final HttpRequest request = new BasicHttpRequest("GET", "/"); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } @Test public void testBasicAuthenticationUsernameWithBlank() throws Exception { final BasicScheme authscheme = new BasicScheme(); final HttpHost host = new HttpHost("somehost", 80); final HttpRequest request = new BasicHttpRequest("GET", "/"); authscheme.initPreemptive(new UsernamePasswordCredentials("blah blah", null)); authscheme.generateAuthResponse(host, request, null); } @Test public void testBasicAuthenticationUsernameWithTab() throws Exception { final BasicScheme authscheme = new BasicScheme(); final HttpHost host = new HttpHost("somehost", 80); final HttpRequest request = new BasicHttpRequest("GET", "/"); authscheme.initPreemptive(new UsernamePasswordCredentials("blah\tblah", null)); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } @Test public void testBasicAuthenticationUsernameWithColon() throws Exception { final BasicScheme authscheme = new BasicScheme(); final HttpHost host = new HttpHost("somehost", 80); final HttpRequest request = new BasicHttpRequest("GET", "/"); authscheme.initPreemptive(new UsernamePasswordCredentials("blah:blah", null)); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } } TestCredentialsProviders.java000066400000000000000000000215671434266521000404030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link org.apache.hc.client5.http.auth.CredentialsProvider} implementations. */ public class TestCredentialsProviders { public final static Credentials CREDS1 = new UsernamePasswordCredentials("user1", "pass1".toCharArray()); public final static Credentials CREDS2 = new UsernamePasswordCredentials("user2", "pass2".toCharArray()); public final static AuthScope SCOPE1 = new AuthScope(null, null, -1, "realm1", null); public final static AuthScope SCOPE2 = new AuthScope(null, null, -1, "realm2", null); public final static AuthScope BOGUS = new AuthScope(null, null, -1, "bogus", null); public final static AuthScope DEFSCOPE = new AuthScope(null, "host", -1, "realm", null); @Test public void testBasicCredentialsProviderCredentials() { final BasicCredentialsProvider state = new BasicCredentialsProvider(); state.setCredentials(SCOPE1, CREDS1); state.setCredentials(SCOPE2, CREDS2); Assertions.assertEquals(CREDS1, state.getCredentials(SCOPE1, null)); Assertions.assertEquals(CREDS2, state.getCredentials(SCOPE2, null)); } @Test public void testBasicCredentialsProviderNoCredentials() { final BasicCredentialsProvider state = new BasicCredentialsProvider(); Assertions.assertNull(state.getCredentials(BOGUS, null)); } @Test public void testBasicCredentialsProviderDefaultCredentials() { final BasicCredentialsProvider state = new BasicCredentialsProvider(); state.setCredentials(new AuthScope(null, null, -1, null ,null), CREDS1); state.setCredentials(SCOPE2, CREDS2); Assertions.assertEquals(CREDS1, state.getCredentials(BOGUS, null)); } @Test public void testDefaultCredentials() throws Exception { final BasicCredentialsProvider state = new BasicCredentialsProvider(); final Credentials expected = new UsernamePasswordCredentials("name", "pass".toCharArray()); state.setCredentials(new AuthScope(null, null, -1, null ,null), expected); final Credentials got = state.getCredentials(DEFSCOPE, null); Assertions.assertEquals(got, expected); } @Test public void testRealmCredentials() throws Exception { final BasicCredentialsProvider state = new BasicCredentialsProvider(); final Credentials expected = new UsernamePasswordCredentials("name", "pass".toCharArray()); state.setCredentials(DEFSCOPE, expected); final Credentials got = state.getCredentials(DEFSCOPE, null); Assertions.assertEquals(expected, got); } @Test public void testHostCredentials() throws Exception { final BasicCredentialsProvider state = new BasicCredentialsProvider(); final Credentials expected = new UsernamePasswordCredentials("name", "pass".toCharArray()); state.setCredentials(new AuthScope(null, "host", -1, null, null), expected); final Credentials got = state.getCredentials(DEFSCOPE, null); Assertions.assertEquals(expected, got); } @Test public void testWrongHostCredentials() throws Exception { final BasicCredentialsProvider state = new BasicCredentialsProvider(); final Credentials expected = new UsernamePasswordCredentials("name", "pass".toCharArray()); state.setCredentials(new AuthScope(null, "host1", -1, "realm", null), expected); final Credentials got = state.getCredentials(new AuthScope(null, "host2", -1, "realm", null), null); Assertions.assertNotSame(expected, got); } @Test public void testWrongRealmCredentials() throws Exception { final BasicCredentialsProvider state = new BasicCredentialsProvider(); final Credentials cred = new UsernamePasswordCredentials("name", "pass".toCharArray()); state.setCredentials(new AuthScope(null, "host", -1, "realm1", null), cred); final Credentials got = state.getCredentials(new AuthScope(null, "host", -1, "realm2", null), null); Assertions.assertNotSame(cred, got); } @Test public void testMixedCaseHostname() throws Exception { final HttpHost httpHost = new HttpHost("hOsT", 80); final BasicCredentialsProvider state = new BasicCredentialsProvider(); final Credentials expected = new UsernamePasswordCredentials("name", "pass".toCharArray()); state.setCredentials(new AuthScope(httpHost), expected); final Credentials got = state.getCredentials(DEFSCOPE, null); Assertions.assertEquals(expected, got); } @Test public void testCredentialsMatching() { final Credentials creds1 = new UsernamePasswordCredentials("name1", "pass1".toCharArray()); final Credentials creds2 = new UsernamePasswordCredentials("name2", "pass2".toCharArray()); final Credentials creds3 = new UsernamePasswordCredentials("name3", "pass3".toCharArray()); final AuthScope scope1 = new AuthScope(null, null, -1, null, null); final AuthScope scope2 = new AuthScope(null, null, -1, "somerealm", null); final AuthScope scope3 = new AuthScope(null, "somehost", -1, null, null); final BasicCredentialsProvider state = new BasicCredentialsProvider(); state.setCredentials(scope1, creds1); state.setCredentials(scope2, creds2); state.setCredentials(scope3, creds3); Credentials got = state.getCredentials(new AuthScope("http", "someotherhost", 80, "someotherrealm", StandardAuthScheme.BASIC), null); Credentials expected = creds1; Assertions.assertEquals(expected, got); got = state.getCredentials(new AuthScope("http", "someotherhost", 80, "somerealm", StandardAuthScheme.BASIC), null); expected = creds2; Assertions.assertEquals(expected, got); got = state.getCredentials(new AuthScope("http", "somehost", 80, "someotherrealm", StandardAuthScheme.BASIC), null); expected = creds3; Assertions.assertEquals(expected, got); } @Test public void testSingleCredentialsProvider() { final Credentials creds1 = new UsernamePasswordCredentials("name1", "pass1".toCharArray()); final CredentialsProvider credentialsProvider1 = new SingleCredentialsProvider(new AuthScope(null, null, -1, null, null), creds1); Assertions.assertEquals(creds1, credentialsProvider1.getCredentials(new AuthScope(null, null, -1, null, null), null)); Assertions.assertEquals(creds1, credentialsProvider1.getCredentials(new AuthScope("http", "someotherhost", 80, "somerealm", StandardAuthScheme.BASIC), null)); final CredentialsProvider credentialsProvider2 = new SingleCredentialsProvider(new AuthScope(null, "somehost", 80, null, null), creds1); Assertions.assertEquals(creds1, credentialsProvider2.getCredentials(new AuthScope(null, null, -1, null, null), null)); Assertions.assertEquals(creds1, credentialsProvider2.getCredentials(new AuthScope(null, "somehost", 80, null, null), null)); Assertions.assertEquals(creds1, credentialsProvider2.getCredentials(new AuthScope("http", "somehost", 80, "somerealm", StandardAuthScheme.BASIC), null)); Assertions.assertNull(credentialsProvider2.getCredentials(new AuthScope(null, "someotherhost", 80, null, null), null)); Assertions.assertNull(credentialsProvider2.getCredentials(new AuthScope(null, "somehost", 8080, null, null), null)); } } TestDigestScheme.java000066400000000000000000001067611434266521000366140ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.MalformedChallengeException; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HeaderElement; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicHeaderValueParser; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.ParserCursor; import org.apache.hc.core5.util.CharArrayBuffer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Test Methods for DigestScheme Authentication. */ public class TestDigestScheme { private static AuthChallenge parse(final String s) throws ParseException { final CharArrayBuffer buffer = new CharArrayBuffer(s.length()); buffer.append(s); final ParserCursor cursor = new ParserCursor(0, buffer.length()); final List authChallenges = AuthChallengeParser.INSTANCE.parse(ChallengeType.TARGET, buffer, cursor); Assertions.assertEquals(1, authChallenges.size()); return authChallenges.get(0); } @Test public void testDigestAuthenticationEmptyChallenge1() throws Exception { final AuthChallenge authChallenge = parse(StandardAuthScheme.DIGEST); final AuthScheme authscheme = new DigestScheme(); Assertions.assertThrows(MalformedChallengeException.class, () -> authscheme.processChallenge(authChallenge, null)); } @Test public void testDigestAuthenticationEmptyChallenge2() throws Exception { final AuthChallenge authChallenge = parse(StandardAuthScheme.DIGEST + " "); final AuthScheme authscheme = new DigestScheme(); Assertions.assertThrows(MalformedChallengeException.class, () -> authscheme.processChallenge(authChallenge, null)); } @Test public void testDigestAuthenticationWithDefaultCreds() throws Exception { final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertTrue(authscheme.isChallengeComplete()); Assertions.assertFalse(authscheme.isConnectionBased()); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals("username", table.get("username")); Assertions.assertEquals("realm1", table.get("realm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); Assertions.assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response")); } @Test public void testDigestAuthentication() throws Exception { final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals("username", table.get("username")); Assertions.assertEquals("realm1", table.get("realm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); Assertions.assertEquals("e95a7ddf37c2eab009568b1ed134f89a", table.get("response")); } @Test public void testDigestAuthenticationInvalidInput() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertThrows(NullPointerException.class, () -> authscheme.isResponseReady(null, credentialsProvider, null)); Assertions.assertThrows(NullPointerException.class, () -> authscheme.isResponseReady(host, null, null)); Assertions.assertThrows(NullPointerException.class, () -> authscheme.generateAuthResponse(host, null, null)); } @Test public void testDigestAuthenticationWithSHA() throws Exception { final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", " + "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "algorithm=SHA"; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals("username", table.get("username")); Assertions.assertEquals("realm1", table.get("realm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); Assertions.assertEquals("8769e82e4e28ecc040b969562b9050580c6d186d", table.get("response")); } @Test public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exception { final HttpRequest request = new BasicHttpRequest("Simple", "/?param=value"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals("username", table.get("username")); Assertions.assertEquals("realm1", table.get("realm")); Assertions.assertEquals("/?param=value", table.get("uri")); Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); Assertions.assertEquals("a847f58f5fef0bc087bcb9c3eb30e042", table.get("response")); } @Test public void testDigestAuthenticationNoRealm() throws Exception { final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " no-realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } @Test public void testDigestAuthenticationNoNonce() throws Exception { final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", no-nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } /** * Test digest authentication using the MD5-sess algorithm. */ @Test public void testDigestAuthenticationMD5Sess() throws Exception { // Example using Digest auth with MD5-sess final String realm="realm"; final String username="username"; final String password="password"; final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, realm, null), username, password.toCharArray()) .build(); final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", " + "nonce=\"" + nonce + "\", " + "opaque=\"SomeString\", " + "stale=false, " + "algorithm=MD5-sess, " + "qop=\"auth,auth-int\""; // we pass both but expect auth to be used final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertTrue(authResponse.indexOf("nc=00000001") > 0); // test for quotes Assertions.assertTrue(authResponse.indexOf("qop=auth") > 0); // test for quotes final Map table = parseAuthResponse(authResponse); Assertions.assertEquals(username, table.get("username")); Assertions.assertEquals(realm, table.get("realm")); Assertions.assertEquals("MD5-sess", table.get("algorithm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals(nonce, table.get("nonce")); Assertions.assertEquals(1, Integer.parseInt(table.get("nc"),16)); Assertions.assertNotNull(table.get("cnonce")); Assertions.assertEquals("SomeString", table.get("opaque")); Assertions.assertEquals("auth", table.get("qop")); //@TODO: add better check Assertions.assertNotNull(table.get("response")); } /** * Test digest authentication using the MD5-sess algorithm. */ @Test public void testDigestAuthenticationMD5SessNoQop() throws Exception { // Example using Digest auth with MD5-sess final String realm="realm"; final String username="username"; final String password="password"; final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, realm, null), username, password.toCharArray()) .build(); final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", " + "nonce=\"" + nonce + "\", " + "opaque=\"SomeString\", " + "stale=false, " + "algorithm=MD5-sess"; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals(username, table.get("username")); Assertions.assertEquals(realm, table.get("realm")); Assertions.assertEquals("MD5-sess", table.get("algorithm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals(nonce, table.get("nonce")); Assertions.assertNull(table.get("nc")); Assertions.assertEquals("SomeString", table.get("opaque")); Assertions.assertNull(table.get("qop")); //@TODO: add better check Assertions.assertNotNull(table.get("response")); } /** * Test digest authentication with unknown qop value */ @Test public void testDigestAuthenticationMD5SessUnknownQop() throws Exception { // Example using Digest auth with MD5-sess final String realm="realm"; final String username="username"; final String password="password"; final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, realm, null), username, password.toCharArray()) .build(); final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", " + "nonce=\"" + nonce + "\", " + "opaque=\"SomeString\", " + "stale=false, " + "algorithm=MD5-sess, " + "qop=\"stuff\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } /** * Test digest authentication with unknown qop value */ @Test public void testDigestAuthenticationUnknownAlgo() throws Exception { // Example using Digest auth with MD5-sess final String realm="realm"; final String username="username"; final String password="password"; final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, realm, null), username, password.toCharArray()) .build(); final String challenge=StandardAuthScheme.DIGEST + " realm=\"" + realm + "\", " + "nonce=\"" + nonce + "\", " + "opaque=\"SomeString\", " + "stale=false, " + "algorithm=stuff, " + "qop=\"auth\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } @Test public void testDigestAuthenticationWithStaleNonce() throws Exception { final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", " + "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", stale=\"true\""; final AuthChallenge authChallenge = parse(challenge); final AuthScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertFalse(authscheme.isChallengeComplete()); } private static Map parseAuthResponse(final String authResponse) { if (!authResponse.startsWith(StandardAuthScheme.DIGEST + " ")) { return null; } final String s = authResponse.substring(7); final ParserCursor cursor = new ParserCursor(0, s.length()); final HeaderElement[] elements = BasicHeaderValueParser.INSTANCE.parseElements(s, cursor); final Map map = new HashMap<>(elements.length); for (final HeaderElement element : elements) { map.put(element.getName(), element.getValue()); } return map; } @Test public void testDigestNouceCount() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge1 = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth"; final AuthChallenge authChallenge1 = parse(challenge1); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge1, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse1 = authscheme.generateAuthResponse(host, request, null); final Map table1 = parseAuthResponse(authResponse1); Assertions.assertEquals("00000001", table1.get("nc")); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse2 = authscheme.generateAuthResponse(host, request, null); final Map table2 = parseAuthResponse(authResponse2); Assertions.assertEquals("00000002", table2.get("nc")); final String challenge2 = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth"; final AuthChallenge authChallenge2 = parse(challenge2); authscheme.processChallenge(authChallenge2, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse3 = authscheme.generateAuthResponse(host, request, null); final Map table3 = parseAuthResponse(authResponse3); Assertions.assertEquals("00000003", table3.get("nc")); final String challenge3 = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth"; final AuthChallenge authChallenge3 = parse(challenge3); authscheme.processChallenge(authChallenge3, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse4 = authscheme.generateAuthResponse(host, request, null); final Map table4 = parseAuthResponse(authResponse4); Assertions.assertEquals("00000001", table4.get("nc")); } @Test public void testDigestMD5SessA1AndCnonceConsistency() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpRequest request = new BasicHttpRequest("GET", "/"); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "subnet.domain.com", null), "username", "password".toCharArray()) .build(); final String challenge1 = StandardAuthScheme.DIGEST + " qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " + "charset=utf-8, realm=\"subnet.domain.com\""; final AuthChallenge authChallenge1 = parse(challenge1); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge1, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse1 = authscheme.generateAuthResponse(host, request, null); final Map table1 = parseAuthResponse(authResponse1); Assertions.assertEquals("00000001", table1.get("nc")); final String cnonce1 = authscheme.getCnonce(); final String sessionKey1 = authscheme.getA1(); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse2 = authscheme.generateAuthResponse(host, request, null); final Map table2 = parseAuthResponse(authResponse2); Assertions.assertEquals("00000002", table2.get("nc")); final String cnonce2 = authscheme.getCnonce(); final String sessionKey2 = authscheme.getA1(); Assertions.assertEquals(cnonce1, cnonce2); Assertions.assertEquals(sessionKey1, sessionKey2); final String challenge2 = StandardAuthScheme.DIGEST + " qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " + "charset=utf-8, realm=\"subnet.domain.com\""; final AuthChallenge authChallenge2 = parse(challenge2); authscheme.processChallenge(authChallenge2, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse3 = authscheme.generateAuthResponse(host, request, null); final Map table3 = parseAuthResponse(authResponse3); Assertions.assertEquals("00000003", table3.get("nc")); final String cnonce3 = authscheme.getCnonce(); final String sessionKey3 = authscheme.getA1(); Assertions.assertEquals(cnonce1, cnonce3); Assertions.assertEquals(sessionKey1, sessionKey3); final String challenge3 = StandardAuthScheme.DIGEST + " qop=\"auth\", algorithm=MD5-sess, nonce=\"fedcba0987654321\", " + "charset=utf-8, realm=\"subnet.domain.com\""; final AuthChallenge authChallenge3 = parse(challenge3); authscheme.processChallenge(authChallenge3, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse4 = authscheme.generateAuthResponse(host, request, null); final Map table4 = parseAuthResponse(authResponse4); Assertions.assertEquals("00000001", table4.get("nc")); final String cnonce4 = authscheme.getCnonce(); final String sessionKey4 = authscheme.getA1(); Assertions.assertNotEquals(cnonce1, cnonce4); Assertions.assertNotEquals(sessionKey1, sessionKey4); } @Test public void testHttpEntityDigest() throws Exception { final HttpEntityDigester digester = new HttpEntityDigester(MessageDigest.getInstance("MD5")); Assertions.assertNull(digester.getDigest()); digester.write('a'); digester.write('b'); digester.write('c'); digester.write(0xe4); digester.write(0xf6); digester.write(0xfc); digester.write(new byte[] { 'a', 'b', 'c'}); Assertions.assertNull(digester.getDigest()); digester.close(); Assertions.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.formatHex(digester.getDigest())); Assertions.assertThrows(IOException.class, () -> digester.write('a')); Assertions.assertThrows(IOException.class, () -> digester.write(new byte[] { 'a', 'b', 'c'})); } @Test public void testDigestAuthenticationQopAuthInt() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest("Post", "/"); request.setEntity(new StringEntity("abc\u00e4\u00f6\u00fcabc", StandardCharsets.ISO_8859_1)); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth,auth-int\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertEquals("Post:/:acd2b59cd01c7737d8069015584c6cac", authscheme.getA2()); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals("username", table.get("username")); Assertions.assertEquals("realm1", table.get("realm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals("auth-int", table.get("qop")); Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); } @Test public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception { final HttpRequest request = new BasicHttpRequest("Post", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth-int\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertEquals("Post:/:d41d8cd98f00b204e9800998ecf8427e", authscheme.getA2()); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals("username", table.get("username")); Assertions.assertEquals("realm1", table.get("realm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals("auth-int", table.get("qop")); Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); } @Test public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest("Post", "/"); request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1, ContentType.DEFAULT_TEXT)); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth,auth-int\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); final String authResponse = authscheme.generateAuthResponse(host, request, null); Assertions.assertEquals("Post:/", authscheme.getA2()); final Map table = parseAuthResponse(authResponse); Assertions.assertEquals("username", table.get("username")); Assertions.assertEquals("realm1", table.get("realm")); Assertions.assertEquals("/", table.get("uri")); Assertions.assertEquals("auth", table.get("qop")); Assertions.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); } @Test public void testParameterCaseSensitivity() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "-", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " Realm=\"-\", " + "nonce=\"YjYuNGYyYmJhMzUuY2I5ZDhlZDE5M2ZlZDM 1Mjk3NGJkNTIyYjgyNTcwMjQ=\", " + "opaque=\"98700A3D9CE17065E2246B41035C6609\", qop=\"auth\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertEquals("-", authscheme.getRealm()); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); authscheme.generateAuthResponse(host, request, null); } @Test public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest("Post", "/"); request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1, ContentType.DEFAULT_TEXT)); final HttpHost host = new HttpHost("somehost", 80); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(host, "realm1", null), "username", "password".toCharArray()) .build(); final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth-int\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); authscheme.processChallenge(authChallenge, null); Assertions.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null)); Assertions.assertThrows(AuthenticationException.class, () -> authscheme.generateAuthResponse(host, request, null)); } @Test public void testSerialization() throws Exception { final String challenge = StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth,auth-int\""; final AuthChallenge authChallenge = parse(challenge); final DigestScheme digestScheme = new DigestScheme(); digestScheme.processChallenge(authChallenge, null); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(buffer); out.writeObject(digestScheme); out.flush(); final byte[] raw = buffer.toByteArray(); final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw)); final DigestScheme authScheme = (DigestScheme) in.readObject(); Assertions.assertEquals(digestScheme.getName(), authScheme.getName()); Assertions.assertEquals(digestScheme.getRealm(), authScheme.getRealm()); Assertions.assertEquals(digestScheme.isChallengeComplete(), authScheme.isChallengeComplete()); Assertions.assertEquals(digestScheme.getA1(), authScheme.getA1()); Assertions.assertEquals(digestScheme.getA2(), authScheme.getA2()); Assertions.assertEquals(digestScheme.getCnonce(), authScheme.getCnonce()); } } TestHttpAuthenticator.java000066400000000000000000000605751434266521000377240ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.util.LinkedList; import java.util.Queue; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.AuthStateCacheable; import org.apache.hc.client5.http.auth.AuthenticationException; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Answers; import org.mockito.Mockito; @SuppressWarnings({"boxing","static-access"}) public class TestHttpAuthenticator { @AuthStateCacheable abstract class CacheableAuthState implements AuthScheme { @Override public String getName() { return StandardAuthScheme.BASIC; } } private AuthExchange authExchange; private CacheableAuthState authScheme; private HttpContext context; private HttpHost defaultHost; private CredentialsProvider credentialsProvider; private Lookup authSchemeRegistry; private HttpAuthenticator httpAuthenticator; @BeforeEach public void setUp() throws Exception { this.authExchange = new AuthExchange(); this.authScheme = Mockito.mock(CacheableAuthState.class, Mockito.withSettings() .defaultAnswer(Answers.CALLS_REAL_METHODS)); Mockito.when(this.authScheme.isChallengeComplete()).thenReturn(Boolean.TRUE); this.context = new BasicHttpContext(); this.defaultHost = new HttpHost("localhost", 80); this.credentialsProvider = Mockito.mock(CredentialsProvider.class); this.context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credentialsProvider); this.authSchemeRegistry = RegistryBuilder.create() .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE) .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE) .register(StandardAuthScheme.NTLM, NTLMSchemeFactory.INSTANCE).build(); this.context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry); this.httpAuthenticator = new HttpAuthenticator(); } @Test public void testUpdateAuthExchange() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test"); Assertions.assertTrue(this.httpAuthenticator.isChallenged( this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context)); } @Test public void testAuthenticationRequestedAfterSuccess() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test"); this.authExchange.select(this.authScheme); this.authExchange.setState(AuthExchange.State.SUCCESS); Assertions.assertTrue(this.httpAuthenticator.isChallenged( this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context)); } @Test public void testAuthenticationNotRequestedUnchallenged() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); Assertions.assertFalse(this.httpAuthenticator.isChallenged( this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.UNCHALLENGED, this.authExchange.getState()); } @Test public void testAuthenticationNotRequestedSuccess1() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); this.authExchange.select(this.authScheme); this.authExchange.setState(AuthExchange.State.CHALLENGED); Assertions.assertFalse(this.httpAuthenticator.isChallenged( this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.SUCCESS, this.authExchange.getState()); } @Test public void testAuthenticationNotRequestedSuccess2() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); this.authExchange.select(this.authScheme); this.authExchange.setState(AuthExchange.State.HANDSHAKE); Assertions.assertFalse(this.httpAuthenticator.isChallenged( this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.SUCCESS, this.authExchange.getState()); } @Test public void testAuthentication() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\"")); final Credentials credentials = new UsernamePasswordCredentials("user", "pass".toCharArray()); Mockito.when(this.credentialsProvider.getCredentials(Mockito.any(), Mockito.any())).thenReturn(credentials); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertTrue(this.httpAuthenticator.updateAuthState(host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.CHALLENGED, this.authExchange.getState()); final Queue options = this.authExchange.getAuthOptions(); Assertions.assertNotNull(options); final AuthScheme authScheme1 = options.poll(); Assertions.assertNotNull(authScheme1); Assertions.assertEquals(StandardAuthScheme.DIGEST, authScheme1.getName()); final AuthScheme authScheme2 = options.poll(); Assertions.assertNotNull(authScheme2); Assertions.assertEquals(StandardAuthScheme.BASIC, authScheme2.getName()); Assertions.assertNull(options.poll()); } @Test public void testAuthenticationCredentialsForBasic() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"1234\"")); final Credentials credentials = new UsernamePasswordCredentials("user", "pass".toCharArray()); Mockito.when(this.credentialsProvider.getCredentials(Mockito.eq(new AuthScope(host, "test", StandardAuthScheme.BASIC)), Mockito.any())).thenReturn(credentials); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertTrue(this.httpAuthenticator.updateAuthState(host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.CHALLENGED, this.authExchange.getState()); final Queue options = this.authExchange.getAuthOptions(); Assertions.assertNotNull(options); final AuthScheme authScheme1 = options.poll(); Assertions.assertNotNull(authScheme1); Assertions.assertEquals(StandardAuthScheme.BASIC, authScheme1.getName()); Assertions.assertNull(options.poll()); } @Test public void testAuthenticationNoChallenges() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertFalse(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); } @Test public void testAuthenticationNoSupportedChallenges() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "This realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "That realm=\"realm1\", nonce=\"1234\"")); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertFalse(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); } @Test public void testAuthenticationNoCredentials() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"1234\"")); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertFalse(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); } @Test public void testAuthenticationFailed() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"1234\"")); this.authExchange.setState(AuthExchange.State.CHALLENGED); this.authExchange.select(this.authScheme); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertFalse(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.FAILURE, this.authExchange.getState()); } @Test public void testAuthenticationFailedPreviously() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"1234\"")); this.authExchange.setState(AuthExchange.State.FAILURE); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertFalse(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.FAILURE, this.authExchange.getState()); } @Test public void testAuthenticationFailure() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\"")); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); this.authExchange.setState(AuthExchange.State.CHALLENGED); this.authExchange.select(new BasicScheme()); Assertions.assertFalse(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.FAILURE, this.authExchange.getState()); } @Test public void testAuthenticationHandshaking() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", stale=true, nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\"")); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); this.authExchange.setState(AuthExchange.State.CHALLENGED); this.authExchange.select(new DigestScheme()); Assertions.assertTrue(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.HANDSHAKE, this.authExchange.getState()); } @Test public void testAuthenticationNoMatchingChallenge() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.DIGEST + " realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\"")); final Credentials credentials = new UsernamePasswordCredentials("user", "pass".toCharArray()); Mockito.when(this.credentialsProvider.getCredentials(Mockito.eq(new AuthScope(host, "realm1", StandardAuthScheme.DIGEST)), Mockito.any())).thenReturn(credentials); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); this.authExchange.setState(AuthExchange.State.CHALLENGED); this.authExchange.select(new BasicScheme()); Assertions.assertTrue(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.CHALLENGED, this.authExchange.getState()); final Queue options = this.authExchange.getAuthOptions(); Assertions.assertNotNull(options); final AuthScheme authScheme1 = options.poll(); Assertions.assertNotNull(authScheme1); Assertions.assertEquals(StandardAuthScheme.DIGEST, authScheme1.getName()); Assertions.assertNull(options.poll()); } @Test public void testAuthenticationException() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "blah blah blah")); this.authExchange.setState(AuthExchange.State.CHALLENGED); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); Assertions.assertFalse(this.httpAuthenticator.updateAuthState( host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context)); Assertions.assertEquals(AuthExchange.State.UNCHALLENGED, this.authExchange.getState()); Assertions.assertNull(this.authExchange.getAuthScheme()); } @Test public void testAuthFailureState() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); this.authExchange.setState(AuthExchange.State.FAILURE); this.authExchange.select(this.authScheme); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authExchange, context); Assertions.assertFalse(request.containsHeader(HttpHeaders.AUTHORIZATION)); Mockito.verify(this.authScheme, Mockito.never()).generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class)); } @Test public void testAuthChallengeStateNoOption() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); this.authExchange.setState(AuthExchange.State.CHALLENGED); this.authExchange.select(this.authScheme); Mockito.when(this.authScheme.generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class))).thenReturn("stuff"); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authExchange, context); Assertions.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); } @Test public void testAuthChallengeStateOneOptions() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); this.authExchange.setState(AuthExchange.State.CHALLENGED); final LinkedList authOptions = new LinkedList<>(); authOptions.add(this.authScheme); this.authExchange.setOptions(authOptions); Mockito.when(this.authScheme.generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class))).thenReturn("stuff"); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authExchange, context); Assertions.assertSame(this.authScheme, this.authExchange.getAuthScheme()); Assertions.assertNull(this.authExchange.getAuthOptions()); Assertions.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); } @Test public void testAuthChallengeStateMultipleOption() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); this.authExchange.setState(AuthExchange.State.CHALLENGED); final LinkedList authOptions = new LinkedList<>(); final AuthScheme authScheme1 = Mockito.mock(AuthScheme.class); Mockito.doThrow(new AuthenticationException()).when(authScheme1).generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class)); final AuthScheme authScheme2 = Mockito.mock(AuthScheme.class); Mockito.when(authScheme2.generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class))).thenReturn("stuff"); authOptions.add(authScheme1); authOptions.add(authScheme2); this.authExchange.setOptions(authOptions); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authExchange, context); Assertions.assertSame(authScheme2, this.authExchange.getAuthScheme()); Assertions.assertNull(this.authExchange.getAuthOptions()); Assertions.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); } @Test public void testAuthSuccess() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); this.authExchange.setState(AuthExchange.State.SUCCESS); this.authExchange.select(this.authScheme); Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.FALSE); Mockito.when(this.authScheme.generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class))).thenReturn("stuff"); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authExchange, context); Assertions.assertSame(this.authScheme, this.authExchange.getAuthScheme()); Assertions.assertNull(this.authExchange.getAuthOptions()); Assertions.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); } @Test public void testAuthSuccessConnectionBased() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); this.authExchange.setState(AuthExchange.State.SUCCESS); this.authExchange.select(this.authScheme); Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.TRUE); Mockito.when(this.authScheme.generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class))).thenReturn("stuff"); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authExchange, context); Assertions.assertFalse(request.containsHeader(HttpHeaders.AUTHORIZATION)); Mockito.verify(this.authScheme, Mockito.never()).generateAuthResponse( Mockito.eq(defaultHost), Mockito.any(HttpRequest.class), Mockito.any(HttpContext.class)); } } TestNTLMEngineImpl.java000066400000000000000000000362041434266521000367640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.Random; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestNTLMEngineImpl { @Test public void testMD4() throws Exception { checkMD4("", "31d6cfe0d16ae931b73c59d7e0c089c0"); checkMD4("a", "bde52cb31de33e46245e05fbdbd6fb24"); checkMD4("abc", "a448017aaf21d8525fc10ae87aa6729d"); checkMD4("message digest", "d9130a8164549fe818874806e1c7014b"); checkMD4("abcdefghijklmnopqrstuvwxyz", "d79e1c308aa5bbcdeea8ed63df412da9"); checkMD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "043f8582f241db351ce627e153e7f0e4"); checkMD4( "12345678901234567890123456789012345678901234567890123456789012345678901234567890", "e33b4ddc9c38f2199c3e7b164fcc0536"); } /* Test suite helper */ static byte toNibble(final char c) { if (c >= 'a' && c <= 'f') { return (byte) (c - 'a' + 0x0a); } if (c >= 'A' && c <= 'F') { return (byte) (c - 'A' + 0x0a); } return (byte) (c - '0'); } /* Test suite helper */ static byte[] toBytes(final String hex) { final byte[] rval = new byte[hex.length() / 2]; int i = 0; while (i < rval.length) { rval[i] = (byte) ((toNibble(hex.charAt(i * 2)) << 4) | (toNibble(hex .charAt(i * 2 + 1)))); i++; } return rval; } /* Test suite MD4 helper */ static void checkMD4(final String input, final String hexOutput) throws Exception { final NTLMEngineImpl.MD4 md4; md4 = new NTLMEngineImpl.MD4(); md4.update(input.getBytes(StandardCharsets.US_ASCII)); final byte[] answer = md4.getOutput(); final byte[] correctAnswer = toBytes(hexOutput); if (answer.length != correctAnswer.length) { throw new Exception("Answer length disagrees for MD4('" + input + "')"); } int i = 0; while (i < answer.length) { if (answer[i] != correctAnswer[i]) { throw new Exception("Answer value for MD4('" + input + "') disagrees at position " + i); } i++; } } @Test public void testLMResponse() throws Exception { final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen( new Random(1234), 1234L, null, null, "SecREt01".toCharArray(), toBytes("0123456789abcdef"), null, null, null, null, null, null); checkArraysMatch(toBytes("c337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56"), gen.getLMResponse()); } @Test public void testNTLMResponse() throws Exception { final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen( new Random(1234), 1234L, null, null, "SecREt01".toCharArray(), toBytes("0123456789abcdef"), null, null, null, null, null, null); checkArraysMatch(toBytes("25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6"), gen.getNTLMResponse()); } @Test public void testLMv2Response() throws Exception { final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen( new Random(1234), 1234L, "DOMAIN", "user", "SecREt01".toCharArray(), toBytes("0123456789abcdef"), "DOMAIN", null, toBytes("ffffff0011223344"), toBytes("ffffff0011223344"), null, null); checkArraysMatch(toBytes("d6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344"), gen.getLMv2Response()); } @Test public void testNTLMv2Response() throws Exception { final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen( new Random(1234), 1234L, "DOMAIN", "user", "SecREt01".toCharArray(), toBytes("0123456789abcdef"), "DOMAIN", toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"), toBytes("ffffff0011223344"), toBytes("ffffff0011223344"), null, toBytes("0090d336b734c301")); checkArraysMatch(toBytes("01010000000000000090d336b734c301ffffff00112233440000000002000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d000000000000000000"), gen.getNTLMv2Blob()); checkArraysMatch(toBytes("cbabbca713eb795d04c97abc01ee498301010000000000000090d336b734c301ffffff00112233440000000002000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d000000000000000000"), gen.getNTLMv2Response()); } @Test public void testLM2SessionResponse() throws Exception { final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen( new Random(1234), 1234L, "DOMAIN", "user", "SecREt01".toCharArray(), toBytes("0123456789abcdef"), "DOMAIN", toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"), toBytes("ffffff0011223344"), toBytes("ffffff0011223344"), null, toBytes("0090d336b734c301")); checkArraysMatch(toBytes("ffffff001122334400000000000000000000000000000000"), gen.getLM2SessionResponse()); } @Test public void testNTLM2SessionResponse() throws Exception { final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen( new Random(1234), 1234L, "DOMAIN", "user", "SecREt01".toCharArray(), toBytes("0123456789abcdef"), "DOMAIN", toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"), toBytes("ffffff0011223344"), toBytes("ffffff0011223344"), null, toBytes("0090d336b734c301")); checkArraysMatch(toBytes("10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455"), gen.getNTLM2SessionResponse()); } @Test public void testNTLMUserSessionKey() throws Exception { final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen( new Random(1234), 1234L, "DOMAIN", "user", "SecREt01".toCharArray(), toBytes("0123456789abcdef"), "DOMAIN", toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"), toBytes("ffffff0011223344"), toBytes("ffffff0011223344"), null, toBytes("0090d336b734c301")); checkArraysMatch(toBytes("3f373ea8e4af954f14faa506f8eebdc4"), gen.getNTLMUserSessionKey()); } @Test public void testType1Message() throws Exception { final byte[] bytes = new NTLMEngineImpl.Type1Message("myhost", "mydomain").getBytes(); final byte[] bytes2 = toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400"); checkArraysMatch(bytes2, bytes); } @Test public void testType3Message() throws Exception { final byte[] bytes = new NTLMEngineImpl.Type3Message( new Random(1234), 1234L, "me", "mypassword", "myhost", "mydomain".toCharArray(), toBytes("0001020304050607"), 0xffffffff, null,null).getBytes(); checkArraysMatch(toBytes("4E544C4D53535000030000001800180048000000180018006000000004000400780000000C000C007C0000001400140088000000100010009C000000FFFFFFFF0501280A0000000FA86886A5D297814200000000000000000000000000000000EEC7568E00798491244959B9C942F4F367C5CBABEEF546F74D0045006D00790068006F00730074006D007900700061007300730077006F007200640094DDAB1EBB82C9A1AB914CAE6F199644"), bytes); final byte[] bytes2 = new NTLMEngineImpl.Type3Message( new Random(1234), 1234L, "me", "mypassword", "myhost", "mydomain".toCharArray(), toBytes("0001020304050607"), 0xffffffff, "mytarget", toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000")).getBytes(); checkArraysMatch(toBytes("4E544C4D53535000030000001800180048000000920092006000000004000400F20000000C000C00F600000014001400020100001000100016010000FFFFFFFF0501280A0000000F3695F1EA7B164788A437892FA7504320DA2D8CF378EBC83CE856A8FB985BF7783545828A91A13AE8010100000000000020CBFAD5DEB19D01A86886A5D29781420000000002000C0044004F004D00410049004E0001000C005300450052005600450052000400140064006F006D00610069006E002E0063006F006D00030022007300650072007600650072002E0064006F006D00610069006E002E0063006F006D0000000000000000004D0045006D00790068006F00730074006D007900700061007300730077006F0072006400BB1AAD36F11631CC7CBC8800CEEE1C99"), bytes2); } private static final String cannedCert = "-----BEGIN CERTIFICATE-----\n"+ "MIIDIDCCAgigAwIBAgIEOqKaWTANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEQMA4GA1UEBxMH\n"+ "TXkgQ2l0eTEYMBYGA1UEChMPTXkgT3JnYW5pemF0aW9uMRcwFQYDVQQDEw5NeSBBcHBsaWNhdGlvbjAe\n"+ "Fw0xNzAzMTcxNDAyMzRaFw0yNzAzMTUxNDAyMzRaMFIxCzAJBgNVBAYTAlVTMRAwDgYDVQQHEwdNeSBD\n"+ "aXR5MRgwFgYDVQQKEw9NeSBPcmdhbml6YXRpb24xFzAVBgNVBAMTDk15IEFwcGxpY2F0aW9uMIIBIjAN\n"+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArc+mbViBaHeRSt82KrJ5IF+62b/Qob95Lca4DJIislTY\n"+ "vLPIo0R1faBV8BkEeUQwo01srkf3RaGLCHNZnFal4KEzbtiUy6W+n08G5E9w9YG+WSwW2dmjvEI7k2a2\n"+ "xqlaM4NdMKL4ONPXcxfZsMDqxDgpdkaNPKpZ10NDq6rmBTkQw/OSG0z1KLtwLkF1ZQ/3mXdjVzvP83V2\n"+ "g17AqBazb0Z1YHsVKmkGjPqnq3niJH/6Oke4N+5k/1cE5lSJcQNGP0nqeGdJfvqQZ+gk6gH/sOngZL9X\n"+ "hPVkpseAwHa+xuPneDSjibLgLmMt3XGDK6jGfjdp5FWqFvAD5E3LHbW9gwIDAQABMA0GCSqGSIb3DQEB\n"+ "CwUAA4IBAQCpUXUHhl5LyMSO5Q0OktEc9AaFjZtVfknpPde6Zeh35Pqd2354ErvJSBWgzFAphda0oh2s\n"+ "OIAFkM6LJQEnVDTbXDXN+YY8e3gb9ryfh85hkhC0XI9qp17WPSkmw8XgDfvRd6YQgKm1AnLxjOCwG2jg\n"+ "i09iZBIWkW3ZeRAMvWPHHjvq44iZB5ZrEl0apgumS6MxpUzKOr5Pcq0jxJDw2UCj5YloFMNl+UINv2vV\n"+ "aL/DR6ivc61dOfN1E/VNBGkkCk/AogNyucGiFMCq9hd25Y9EbkBBqObYTH1XMX+ufsJh+6hG7KDQ1e/F\n"+ "nRrlhKwM2uRe+aSH0D6/erjDBT7tXvwn\n"+ "-----END CERTIFICATE-----"; @Test public void testType3MessageWithCert() throws Exception { final ByteArrayInputStream fis = new ByteArrayInputStream(cannedCert.getBytes(StandardCharsets.US_ASCII)); final CertificateFactory cf = CertificateFactory.getInstance("X.509"); final Certificate cert = cf.generateCertificate(fis); final byte[] bytes = new NTLMEngineImpl.Type3Message( new Random(1234), 1234L, "me", "mypassword", "myhost", "mydomain".toCharArray(), toBytes("0001020304050607"), 0xffffffff, "mytarget", toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"), cert, toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400"), toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400FFFEFDFCFBFA")).getBytes(); checkArraysMatch(toBytes("4E544C4D53535000030000001800180058000000AE00AE0070000000040004001E0100000C000C0022010000140014002E0100001000100042010000FFFFFFFF0501280A0000000FEEFCCE4187D6CDF1F91C686C4E571D943695F1EA7B164788A437892FA7504320DA2D8CF378EBC83C59D7A3B2951929079B66621D7CF4326B010100000000000020CBFAD5DEB19D01A86886A5D29781420000000002000C0044004F004D00410049004E0001000C005300450052005600450052000400140064006F006D00610069006E002E0063006F006D00030022007300650072007600650072002E0064006F006D00610069006E002E0063006F006D0006000400020000000A00100038EDC0B7EF8D8FE9E1E6A83F6DFEB8FF00000000000000004D0045006D00790068006F00730074006D007900700061007300730077006F0072006400BB1AAD36F11631CC7CBC8800CEEE1C99"), bytes); } @Test public void testRC4() throws Exception { checkArraysMatch(toBytes("e37f97f2544f4d7e"), NTLMEngineImpl.RC4(toBytes("0a003602317a759a"), toBytes("2785f595293f3e2813439d73a223810d"))); } /* Byte array check helper */ static void checkArraysMatch(final byte[] a1, final byte[] a2) throws Exception { Assertions.assertEquals(a1.length,a2.length); for (int i = 0; i < a1.length; i++) { Assertions.assertEquals(a1[i],a2[i]); } } } TestNTLMScheme.java000066400000000000000000000044551434266521000361440ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import org.apache.hc.client5.http.auth.AuthChallenge; import org.apache.hc.client5.http.auth.AuthScheme; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link NTLMScheme}. */ public class TestNTLMScheme { @Test public void testNTLMAuthenticationEmptyProxyChallenge() throws Exception { final AuthChallenge authChallenge = new AuthChallenge(ChallengeType.PROXY, StandardAuthScheme.NTLM); final AuthScheme authScheme = new NTLMScheme(); authScheme.processChallenge(authChallenge, null); Assertions.assertFalse(authScheme.isChallengeComplete(), "Challenge with an empty value received from NTML proxy must not interrupt authentication process."); Assertions.assertTrue(authScheme.toString().contains(NTLMScheme.State.CHALLENGE_RECEIVED.toString()), "Challenge with an empty value received from NTML proxy must transit status of NTLMScheme to CHALLENGE_RECEIVED."); } }TestSystemDefaultCredentialsProvider.java000066400000000000000000000155711434266521000427300ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.auth; import java.net.Authenticator; import java.net.Authenticator.RequestorType; import java.net.InetAddress; import java.net.PasswordAuthentication; import java.net.URL; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.Credentials; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; /** * Simple tests for {@link SystemDefaultCredentialsProvider}. */ public class TestSystemDefaultCredentialsProvider { private final static String PROXY_PROTOCOL1 = "http"; private final static String PROXY_HOST1 = "proxyhost1"; private final static int PROXY_PORT1 = 3128; private final static String PROMPT1 = "HttpClient authentication test prompt"; private final static String TARGET_SCHEME1 = "https"; private final static String TARGET_HOST1 = "targethost1"; private final static int TARGET_PORT1 = 80; private final static PasswordAuthentication AUTH1 = new PasswordAuthentication("testUser", "testPassword".toCharArray()); // It's not possible to mock static Authenticator methods. So we mock a delegate private final class DelegatedAuthenticator extends Authenticator { private final AuthenticatorDelegate authenticatorDelegate; private DelegatedAuthenticator(final AuthenticatorDelegate authenticatorDelegate) { this.authenticatorDelegate = authenticatorDelegate; } @Override protected PasswordAuthentication getPasswordAuthentication() { return authenticatorDelegate.getPasswordAuthentication(getRequestingHost(), getRequestingSite(), getRequestingPort(), getRequestingProtocol(), getRequestingPrompt(), getRequestingScheme(), getRequestingURL(), getRequestorType()); } } private interface AuthenticatorDelegate { PasswordAuthentication getPasswordAuthentication( String host, InetAddress addr, int port, String protocol, String prompt, String scheme, URL url, RequestorType reqType); } @Test public void testSystemCredentialsProviderCredentials() throws Exception { final AuthenticatorDelegate authenticatorDelegate = installAuthenticator(AUTH1); final URL httpRequestUrl = new URL(TARGET_SCHEME1, TARGET_HOST1, TARGET_PORT1, "/"); final AuthScope authScope = new AuthScope(PROXY_PROTOCOL1, PROXY_HOST1, PROXY_PORT1, PROMPT1, StandardAuthScheme.BASIC); final HttpCoreContext coreContext = new HttpCoreContext(); coreContext.setAttribute(HttpCoreContext.HTTP_REQUEST, new HttpGet(httpRequestUrl.toURI())); final Credentials receivedCredentials = new SystemDefaultCredentialsProvider().getCredentials(authScope, coreContext); Mockito.verify(authenticatorDelegate).getPasswordAuthentication(PROXY_HOST1, null, PROXY_PORT1, PROXY_PROTOCOL1, PROMPT1, StandardAuthScheme.BASIC, httpRequestUrl, RequestorType.SERVER); Assertions.assertNotNull(receivedCredentials); Assertions.assertEquals(AUTH1.getUserName(), receivedCredentials.getUserPrincipal().getName()); Assertions.assertEquals(AUTH1.getPassword(), receivedCredentials.getPassword()); } @Test public void testSystemCredentialsProviderNoContext() throws Exception { final AuthenticatorDelegate authenticatorDelegate = installAuthenticator(AUTH1); final AuthScope authScope = new AuthScope(PROXY_PROTOCOL1, PROXY_HOST1, PROXY_PORT1, PROMPT1, StandardAuthScheme.BASIC); final Credentials receivedCredentials = new SystemDefaultCredentialsProvider().getCredentials(authScope, null); Mockito.verify(authenticatorDelegate).getPasswordAuthentication(PROXY_HOST1, null, PROXY_PORT1, PROXY_PROTOCOL1, PROMPT1, StandardAuthScheme.BASIC, null, RequestorType.SERVER); Assertions.assertNotNull(receivedCredentials); Assertions.assertEquals(AUTH1.getUserName(), receivedCredentials.getUserPrincipal().getName()); Assertions.assertEquals(AUTH1.getPassword(), receivedCredentials.getPassword()); } private AuthenticatorDelegate installAuthenticator(final PasswordAuthentication returedAuthentication) { final AuthenticatorDelegate authenticatorDelegate = Mockito.mock(AuthenticatorDelegate.class); Mockito.when(authenticatorDelegate.getPasswordAuthentication(ArgumentMatchers.anyString(), ArgumentMatchers.any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(returedAuthentication); Authenticator.setDefault(new DelegatedAuthenticator(authenticatorDelegate)); return authenticatorDelegate; } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/000077500000000000000000000000001434266521000332715ustar00rootroot00000000000000MockClock.java000066400000000000000000000027041434266521000357250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; public class MockClock implements Clock { private long t = System.currentTimeMillis(); @Override public long getCurrentTime() { return t; } public void setCurrentTime(final long now) { t = now; } } MockConnPoolControl.java000066400000000000000000000066651434266521000377740ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.core5.pool.ConnPoolControl; import org.apache.hc.core5.pool.PoolStats; import org.apache.hc.core5.util.TimeValue; public final class MockConnPoolControl implements ConnPoolControl { private final ConcurrentHashMap maxPerHostMap; private volatile int totalMax; private volatile int defaultMax; public MockConnPoolControl() { super(); this.maxPerHostMap = new ConcurrentHashMap<>(); this.totalMax = 20; this.defaultMax = 2; } @Override public void setMaxTotal(final int max) { this.totalMax = max; } @Override public int getMaxTotal() { return this.totalMax; } @Override public PoolStats getTotalStats() { return new PoolStats(-1, -1, -1, this.totalMax); } @Override public PoolStats getStats(final HttpRoute route) { return new PoolStats(-1, -1, -1, getMaxPerRoute(route)); } @Override public int getDefaultMaxPerRoute() { return this.defaultMax; } @Override public void setDefaultMaxPerRoute(final int max) { this.defaultMax = max; } @Override public void setMaxPerRoute(final HttpRoute route, final int max) { this.maxPerHostMap.put(route, max); } @Override public int getMaxPerRoute(final HttpRoute route) { final Integer max = this.maxPerHostMap.get(route); if (max != null) { return max; } else { return this.defaultMax; } } @Override public void closeIdle(final TimeValue idletime) { } @Override public void closeExpired() { } public void setMaxForRoutes(final Map map) { if (map == null) { return; } this.maxPerHostMap.clear(); this.maxPerHostMap.putAll(map); } @Override public Set getRoutes() { return new HashSet<>(this.maxPerHostMap.keySet()); } @Override public String toString() { return this.maxPerHostMap.toString(); } } TestAIMDBackoffManager.java000066400000000000000000000141611434266521000402010ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Random; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.BackoffManager; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestAIMDBackoffManager { private AIMDBackoffManager impl; private MockConnPoolControl connPerRoute; private HttpRoute route; private MockClock clock; @BeforeEach public void setUp() { connPerRoute = new MockConnPoolControl(); route = new HttpRoute(new HttpHost("localhost", 80)); clock = new MockClock(); impl = new AIMDBackoffManager(connPerRoute, clock); impl.setPerHostConnectionCap(10); } @Test public void isABackoffManager() { assertTrue(impl instanceof BackoffManager); } @Test public void halvesConnectionsOnBackoff() { connPerRoute.setMaxPerRoute(route, 4); impl.backOff(route); assertEquals(2, connPerRoute.getMaxPerRoute(route)); } @Test public void doesNotBackoffBelowOneConnection() { connPerRoute.setMaxPerRoute(route, 1); impl.backOff(route); assertEquals(1, connPerRoute.getMaxPerRoute(route)); } @Test public void increasesByOneOnProbe() { connPerRoute.setMaxPerRoute(route, 2); impl.probe(route); assertEquals(3, connPerRoute.getMaxPerRoute(route)); } @Test public void doesNotIncreaseBeyondPerHostMaxOnProbe() { connPerRoute.setDefaultMaxPerRoute(5); connPerRoute.setMaxPerRoute(route, 5); impl.setPerHostConnectionCap(5); impl.probe(route); assertEquals(5, connPerRoute.getMaxPerRoute(route)); } @Test public void backoffDoesNotAdjustDuringCoolDownPeriod() { connPerRoute.setMaxPerRoute(route, 4); final long now = System.currentTimeMillis(); clock.setCurrentTime(now); impl.backOff(route); final long max = connPerRoute.getMaxPerRoute(route); clock.setCurrentTime(now + 1); impl.backOff(route); assertEquals(max, connPerRoute.getMaxPerRoute(route)); } @Test public void backoffStillAdjustsAfterCoolDownPeriod() { connPerRoute.setMaxPerRoute(route, 8); final long now = System.currentTimeMillis(); clock.setCurrentTime(now); impl.backOff(route); final long max = connPerRoute.getMaxPerRoute(route); clock.setCurrentTime(now + 10 * 1000L); impl.backOff(route); assertTrue(max == 1 || max > connPerRoute.getMaxPerRoute(route)); } @Test public void probeDoesNotAdjustDuringCooldownPeriod() { connPerRoute.setMaxPerRoute(route, 4); final long now = System.currentTimeMillis(); clock.setCurrentTime(now); impl.probe(route); final long max = connPerRoute.getMaxPerRoute(route); clock.setCurrentTime(now + 1); impl.probe(route); assertEquals(max, connPerRoute.getMaxPerRoute(route)); } @Test public void probeStillAdjustsAfterCoolDownPeriod() { connPerRoute.setMaxPerRoute(route, 8); final long now = System.currentTimeMillis(); clock.setCurrentTime(now); impl.probe(route); final long max = connPerRoute.getMaxPerRoute(route); clock.setCurrentTime(now + 10 * 1000L); impl.probe(route); assertTrue(max < connPerRoute.getMaxPerRoute(route)); } @Test public void willBackoffImmediatelyEvenAfterAProbe() { connPerRoute.setMaxPerRoute(route, 8); final long now = System.currentTimeMillis(); clock.setCurrentTime(now); impl.probe(route); final long max = connPerRoute.getMaxPerRoute(route); clock.setCurrentTime(now + 1); impl.backOff(route); assertTrue(connPerRoute.getMaxPerRoute(route) < max); } @Test public void backOffFactorIsConfigurable() { connPerRoute.setMaxPerRoute(route, 10); impl.setBackoffFactor(0.9); impl.backOff(route); assertEquals(9, connPerRoute.getMaxPerRoute(route)); } @Test public void coolDownPeriodIsConfigurable() { long cd = new Random().nextLong() / 2; if (cd < 0) { cd *= -1; } if (cd < 1) { cd++; } final long now = System.currentTimeMillis(); impl.setCoolDown(TimeValue.ofMilliseconds(cd)); clock.setCurrentTime(now); impl.probe(route); final int max0 = connPerRoute.getMaxPerRoute(route); clock.setCurrentTime(now); impl.probe(route); assertEquals(max0, connPerRoute.getMaxPerRoute(route)); clock.setCurrentTime(now + cd + 1); impl.probe(route); assertTrue(max0 < connPerRoute.getMaxPerRoute(route)); } } TestAbstractHttpClientResponseHandler.java000066400000000000000000000115141434266521000434760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InputStream; import org.apache.hc.client5.http.HttpResponseException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** * Unit tests for {@link BasicHttpClientResponseHandler}. */ public class TestAbstractHttpClientResponseHandler { @Test public void testSuccessfulResponse() throws Exception { final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); final HttpEntity entity = new StringEntity("42"); Mockito.when(response.getCode()).thenReturn(200); Mockito.when(response.getEntity()).thenReturn(entity); final AbstractHttpClientResponseHandler handler = new AbstractHttpClientResponseHandler() { @Override public Integer handleEntity(final HttpEntity entity) throws IOException { return Integer.valueOf(new String(EntityUtils.toByteArray(entity))); } }; final Integer number = handler.handleResponse(response); Assertions.assertEquals(42, number.intValue()); } @SuppressWarnings("boxing") @Test public void testUnsuccessfulResponse() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); final HttpEntity entity = Mockito.mock(HttpEntity.class); Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(entity.getContent()).thenReturn(inStream); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(response.getCode()).thenReturn(404); Mockito.when(response.getReasonPhrase()).thenReturn("NOT FOUND"); Mockito.when(response.getEntity()).thenReturn(entity); final BasicHttpClientResponseHandler handler = new BasicHttpClientResponseHandler(); final HttpResponseException exception = Assertions.assertThrows(HttpResponseException.class, () -> handler.handleResponse(response)); Assertions.assertEquals(404, exception.getStatusCode()); Assertions.assertEquals("NOT FOUND", exception.getReasonPhrase()); Assertions.assertEquals("status code: 404, reason phrase: NOT FOUND", exception.getMessage()); Mockito.verify(entity).getContent(); Mockito.verify(inStream).close(); } @SuppressWarnings("boxing") @Test public void testUnsuccessfulResponseEmptyReason() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); final HttpEntity entity = Mockito.mock(HttpEntity.class); Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(entity.getContent()).thenReturn(inStream); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(response.getCode()).thenReturn(404); Mockito.when(response.getEntity()).thenReturn(entity); final BasicHttpClientResponseHandler handler = new BasicHttpClientResponseHandler(); final HttpResponseException exception = Assertions.assertThrows(HttpResponseException.class, () -> handler.handleResponse(response)); Assertions.assertEquals(404, exception.getStatusCode()); Assertions.assertNull(exception.getReasonPhrase()); Assertions.assertEquals("status code: 404", exception.getMessage()); Mockito.verify(entity).getContent(); Mockito.verify(inStream).close(); } } TestBasicResponseHandler.java000066400000000000000000000062271434266521000407620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.InputStream; import org.apache.hc.client5.http.HttpResponseException; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** * Unit tests for {@link BasicHttpClientResponseHandler}. */ @SuppressWarnings("boxing") // test code public class TestBasicResponseHandler { @Test public void testSuccessfulResponse() throws Exception { final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); final HttpEntity entity = new StringEntity("stuff"); Mockito.when(response.getCode()).thenReturn(200); Mockito.when(response.getEntity()).thenReturn(entity); final BasicHttpClientResponseHandler handler = new BasicHttpClientResponseHandler(); final String s = handler.handleResponse(response); Assertions.assertEquals("stuff", s); } @Test public void testUnsuccessfulResponse() throws Exception { final InputStream inStream = Mockito.mock(InputStream.class); final HttpEntity entity = Mockito.mock(HttpEntity.class); Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(entity.getContent()).thenReturn(inStream); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(response.getCode()).thenReturn(404); Mockito.when(response.getEntity()).thenReturn(entity); final BasicHttpClientResponseHandler handler = new BasicHttpClientResponseHandler(); final HttpResponseException exception = Assertions.assertThrows(HttpResponseException.class, () -> handler.handleResponse(response)); Assertions.assertEquals(404, exception.getStatusCode()); Mockito.verify(entity).getContent(); Mockito.verify(inStream).close(); } } TestCloseableHttpClient.java000066400000000000000000000147521434266521000406160ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InputStream; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** * Simple tests for {@link CloseableHttpClient}. */ @SuppressWarnings({"boxing","static-access"}) // test code public class TestCloseableHttpClient { static abstract class NoopCloseableHttpClient extends CloseableHttpClient { @Override protected CloseableHttpResponse doExecute( final HttpHost target, final ClassicHttpRequest request, final HttpContext context) throws IOException { return null; } } private NoopCloseableHttpClient client; private InputStream content; private HttpEntity entity; private ClassicHttpResponse originResponse; private CloseableHttpResponse response; @BeforeEach public void setup() throws Exception { content = Mockito.mock(InputStream.class); entity = Mockito.mock(HttpEntity.class); originResponse = Mockito.mock(ClassicHttpResponse.class); response = CloseableHttpResponse.adapt(originResponse); Mockito.when(entity.getContent()).thenReturn(content); Mockito.when(entity.isStreaming()).thenReturn(Boolean.TRUE); Mockito.when(response.getEntity()).thenReturn(entity); client = Mockito.mock(NoopCloseableHttpClient.class, Mockito.CALLS_REAL_METHODS); } @Test public void testExecuteRequestAbsoluteURI() throws Exception { final HttpGet httpget = new HttpGet("https://somehost:444/stuff"); Mockito.when(client.doExecute( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(response); client.execute(httpget, response -> null); Mockito.verify(client).doExecute( Mockito.eq(new HttpHost("https", "somehost", 444)), Mockito.same(httpget), (HttpContext) Mockito.isNull()); } @Test public void testExecuteRequestRelativeURI() throws Exception { final HttpGet httpget = new HttpGet("/stuff"); Mockito.when(client.doExecute( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(response); client.execute(httpget, response -> null); Mockito.verify(client).doExecute( (HttpHost) Mockito.isNull(), Mockito.same(httpget), (HttpContext) Mockito.isNull()); } @Test public void testExecuteRequestHandleResponse() throws Exception { final HttpGet httpget = new HttpGet("https://somehost:444/stuff"); Mockito.when(client.doExecute( new HttpHost("https", "somehost", 444), httpget, null)).thenReturn(response); final HttpClientResponseHandler handler = Mockito.mock(HttpClientResponseHandler.class); client.execute(httpget, handler); Mockito.verify(client).doExecute( Mockito.eq(new HttpHost("https", "somehost", 444)), Mockito.same(httpget), (HttpContext) Mockito.isNull()); Mockito.verify(handler).handleResponse(response); Mockito.verify(content).close(); } @Test public void testExecuteRequestHandleResponseIOException() throws Exception { final HttpGet httpget = new HttpGet("https://somehost:444/stuff"); Mockito.when(client.doExecute( new HttpHost("https", "somehost", 444), httpget, null)).thenReturn(response); final HttpClientResponseHandler handler = Mockito.mock(HttpClientResponseHandler.class); Mockito.when(handler.handleResponse(response)).thenThrow(new IOException()); Assertions.assertThrows(IOException.class, () -> client.execute(httpget, handler)); Mockito.verify(client).doExecute( Mockito.eq(new HttpHost("https", "somehost", 444)), Mockito.same(httpget), (HttpContext) Mockito.isNull()); Mockito.verify(originResponse).close(); } @Test public void testExecuteRequestHandleResponseHttpException() throws Exception { final HttpGet httpget = new HttpGet("https://somehost:444/stuff"); Mockito.when(client.doExecute( new HttpHost("https", "somehost", 444), httpget, null)).thenReturn(response); final HttpClientResponseHandler handler = Mockito.mock(HttpClientResponseHandler.class); Mockito.when(handler.handleResponse(response)).thenThrow(new RuntimeException()); Assertions.assertThrows(RuntimeException.class, () -> client.execute(httpget, handler)); Mockito.verify(client).doExecute( Mockito.eq(new HttpHost("https", "somehost", 444)), Mockito.same(httpget), (HttpContext) Mockito.isNull()); Mockito.verify(originResponse).close(); } } TestConnectExec.java000066400000000000000000000400411434266521000371120ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Collections; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.impl.TunnelRefusedException; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; @SuppressWarnings({"boxing","static-access"}) // test code public class TestConnectExec { @Mock private ConnectionReuseStrategy reuseStrategy; @Mock private HttpProcessor proxyHttpProcessor; @Mock private AuthenticationStrategy proxyAuthStrategy; @Mock private ExecRuntime execRuntime; @Mock private ExecChain execChain; private ConnectExec exec; private HttpHost target; private HttpHost proxy; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); exec = new ConnectExec(reuseStrategy, proxyHttpProcessor, proxyAuthStrategy, null, true); target = new HttpHost("foo", 80); proxy = new HttpHost("bar", 8888); } @Test public void testExecAcquireConnection() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); try (final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK")) { response.setEntity(EntityBuilder.create() .setStream(new ByteArrayInputStream(new byte[]{})) .build()); } context.setUserToken("Blah"); Mockito.when(execRuntime.isEndpointAcquired()).thenReturn(false); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); exec.execute(request, scope, execChain); Mockito.verify(execRuntime).acquireEndpoint("test", route, "Blah", context); Mockito.verify(execRuntime).connectEndpoint(context); } @Test public void testEstablishDirectRoute() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); exec.execute(request, scope, execChain); Mockito.verify(execRuntime).connectEndpoint(context); Mockito.verify(execRuntime, Mockito.never()).execute( Mockito.anyString(), Mockito.any(), Mockito.any()); } @Test public void testEstablishRouteDirectProxy() throws Exception { final HttpRoute route = new HttpRoute(target, null, proxy, false); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); exec.execute(request, scope, execChain); Mockito.verify(execRuntime).connectEndpoint(context); Mockito.verify(execRuntime, Mockito.never()).execute( Mockito.anyString(), Mockito.any(), Mockito.any()); } @Test public void testEstablishRouteViaProxyTunnel() throws Exception { final HttpRoute route = new HttpRoute(target, null, proxy, true); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.any(), Mockito.any())).thenReturn(response); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); exec.execute(request, scope, execChain); Mockito.verify(execRuntime).connectEndpoint(context); final ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(execRuntime).execute( Mockito.anyString(), reqCaptor.capture(), Mockito.same(context)); final HttpRequest connect = reqCaptor.getValue(); Assertions.assertNotNull(connect); Assertions.assertEquals("CONNECT", connect.getMethod()); Assertions.assertEquals(HttpVersion.HTTP_1_1, connect.getVersion()); Assertions.assertEquals("foo:80", connect.getRequestUri()); } @Test public void testEstablishRouteViaProxyTunnelUnexpectedResponse() throws Exception { final HttpRoute route = new HttpRoute(target, null, proxy, true); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(101, "Lost"); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.any(), Mockito.any())).thenReturn(response); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(HttpException.class, () -> exec.execute(request, scope, execChain)); } @Test public void testEstablishRouteViaProxyTunnelFailure() throws Exception { final HttpRoute route = new HttpRoute(target, null, proxy, true); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(500, "Boom"); response.setEntity(new StringEntity("Ka-boom")); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.any(), Mockito.any())).thenReturn(response); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final TunnelRefusedException exception = Assertions.assertThrows(TunnelRefusedException.class, () -> exec.execute(request, scope, execChain)); Assertions.assertEquals("Ka-boom", exception.getResponseMessage()); Mockito.verify(execRuntime, Mockito.atLeastOnce()).disconnectEndpoint(); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnection() throws Exception { final HttpRoute route = new HttpRoute(target, null, proxy, true); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(407, "Huh?"); response1.setHeader(HttpHeaders.PROXY_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test"); final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(inStream1) .build()); final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK"); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(new AuthScope(proxy), "user", "pass".toCharArray()) .build()); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); Mockito.when(reuseStrategy.keepAlive( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Boolean.TRUE); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.any(), Mockito.any())).thenReturn(response1, response2); Mockito.when(proxyAuthStrategy.select( Mockito.eq(ChallengeType.PROXY), Mockito.any(), Mockito.any())).thenReturn(Collections.singletonList(new BasicScheme())); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); exec.execute(request, scope, execChain); Mockito.verify(execRuntime).connectEndpoint(context); Mockito.verify(inStream1).close(); } @Test public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentConnection() throws Exception { final HttpRoute route = new HttpRoute(target, null, proxy, true); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(407, "Huh?"); response1.setHeader(HttpHeaders.PROXY_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test"); final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(inStream1) .build()); final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK"); final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create() .add(new AuthScope(proxy), "user", "pass".toCharArray()) .build(); context.setCredentialsProvider(credentialsProvider); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.any(), Mockito.any())).thenReturn(response1, response2); Mockito.when(proxyAuthStrategy.select( Mockito.eq(ChallengeType.PROXY), Mockito.any(), Mockito.any())).thenReturn(Collections.singletonList(new BasicScheme())); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); exec.execute(request, scope, execChain); Mockito.verify(execRuntime).connectEndpoint(context); Mockito.verify(inStream1, Mockito.never()).close(); Mockito.verify(execRuntime, Mockito.atLeastOnce()).disconnectEndpoint(); } @Test public void testEstablishRouteViaProxyTunnelMultipleHops() throws Exception { final HttpHost proxy1 = new HttpHost("this", 8888); final HttpHost proxy2 = new HttpHost("that", 8888); final HttpRoute route = new HttpRoute(target, null, new HttpHost[] {proxy1, proxy2}, true, RouteInfo.TunnelType.TUNNELLED, RouteInfo.LayerType.LAYERED); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ConnectionState connectionState = new ConnectionState(); Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connectEndpoint(Mockito.any()); Mockito.when(execRuntime.isEndpointConnected()).thenAnswer(connectionState.isConnectedAnswer()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(HttpException.class, () -> exec.execute(request, scope, execChain)); } static class ConnectionState { private boolean connected; public Answer connectAnswer() { return invocationOnMock -> { connected = true; return null; }; } public Answer isConnectedAnswer() { return invocationOnMock -> connected; } } } TestContentCompressionExec.java000066400000000000000000000235461434266521000413700ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.DecompressingEntity; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.entity.GzipDecompressingEntity; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.Method; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestContentCompressionExec { @Mock private ExecRuntime execRuntime; @Mock private ExecChain execChain; @Mock private ClassicHttpRequest originaRequest; private HttpClientContext context; private HttpHost host; private ExecChain.Scope scope; private ContentCompressionExec impl; @BeforeEach public void setup() { MockitoAnnotations.openMocks(this); host = new HttpHost("somehost", 80); context = HttpClientContext.create(); scope = new ExecChain.Scope("test", new HttpRoute(host), originaRequest, execRuntime, context); impl = new ContentCompressionExec(); } @Test public void testContentEncodingNoEntity() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNull(entity); } @Test public void testNoContentEncoding() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final StringEntity original = new StringEntity("plain stuff"); response.setEntity(original); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity instanceof StringEntity); } @Test public void testGzipContentEncoding() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("GZip").build(); response.setEntity(original); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity instanceof DecompressingEntity); } @Test public void testGzipContentEncodingZeroLength() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("").setContentEncoding("GZip").build(); response.setEntity(original); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity instanceof StringEntity); } @Test public void testXGzipContentEncoding() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("x-gzip").build(); response.setEntity(original); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity instanceof DecompressingEntity); } @Test public void testDeflateContentEncoding() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("deFlaTe").build(); response.setEntity(original); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity instanceof DecompressingEntity); } @Test public void testIdentityContentEncoding() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("identity").build(); response.setEntity(original); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity instanceof StringEntity); } @Test public void testBrotliContentEncoding() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("br").build(); response.setEntity(original); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertTrue(entity instanceof DecompressingEntity); } @Test public void testUnknownContentEncoding() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("whatever").build(); response.setEntity(original); impl = new ContentCompressionExec(false); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); Assertions.assertThrows(HttpException.class, () -> impl.execute(request, scope, execChain)); } @Test public void testContentEncodingRequestParameter() throws Exception { final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("GZip").build(); response.setEntity(original); final RequestConfig config = RequestConfig.custom() .setContentCompressionEnabled(false) .build(); context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); Mockito.when(execChain.proceed(request, scope)).thenReturn(response); impl.execute(request, scope, execChain); final HttpEntity entity = response.getEntity(); Assertions.assertNotNull(entity); Assertions.assertFalse(entity instanceof GzipDecompressingEntity); } } TestCookieIdentityComparator.java000066400000000000000000000125261434266521000416760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import org.apache.hc.client5.http.cookie.CookieIdentityComparator; import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link CookieIdentityComparator}. */ public class TestCookieIdentityComparator { @Test public void testCookieIdentityComparasionByName() { final CookieIdentityComparator comparator = CookieIdentityComparator.INSTANCE; final BasicClientCookie c1 = new BasicClientCookie("name", "value1"); final BasicClientCookie c2 = new BasicClientCookie("name", "value2"); Assertions.assertEquals(0, comparator.compare(c1, c2)); final BasicClientCookie c3 = new BasicClientCookie("name1", "value"); final BasicClientCookie c4 = new BasicClientCookie("name2", "value"); Assertions.assertNotEquals(0, comparator.compare(c3, c4)); } @Test public void testCookieIdentityComparasionByNameAndDomain() { final CookieIdentityComparator comparator = CookieIdentityComparator.INSTANCE; final BasicClientCookie c1 = new BasicClientCookie("name", "value1"); c1.setDomain("www.domain.com"); final BasicClientCookie c2 = new BasicClientCookie("name", "value2"); c2.setDomain("www.domain.com"); Assertions.assertEquals(0, comparator.compare(c1, c2)); final BasicClientCookie c3 = new BasicClientCookie("name", "value1"); c3.setDomain("www.domain.com"); final BasicClientCookie c4 = new BasicClientCookie("name", "value2"); c4.setDomain("domain.com"); Assertions.assertNotEquals(0, comparator.compare(c3, c4)); } @Test public void testCookieIdentityComparasionByNameAndNullDomain() { final CookieIdentityComparator comparator = CookieIdentityComparator.INSTANCE; final BasicClientCookie c1 = new BasicClientCookie("name", "value1"); c1.setDomain(null); final BasicClientCookie c2 = new BasicClientCookie("name", "value2"); c2.setDomain(null); Assertions.assertEquals(0, comparator.compare(c1, c2)); final BasicClientCookie c3 = new BasicClientCookie("name", "value1"); c3.setDomain("www.domain.com"); final BasicClientCookie c4 = new BasicClientCookie("name", "value2"); c4.setDomain(null); Assertions.assertNotEquals(0, comparator.compare(c3, c4)); } @Test public void testCookieIdentityComparasionByNameDomainAndPath() { final CookieIdentityComparator comparator = CookieIdentityComparator.INSTANCE; final BasicClientCookie c1 = new BasicClientCookie("name", "value1"); c1.setDomain("www.domain.com"); c1.setPath("/whatever"); final BasicClientCookie c2 = new BasicClientCookie("name", "value2"); c2.setDomain("www.domain.com"); c2.setPath("/whatever"); Assertions.assertEquals(0, comparator.compare(c1, c2)); final BasicClientCookie c3 = new BasicClientCookie("name", "value1"); c3.setDomain("www.domain.com"); c3.setPath("/whatever"); final BasicClientCookie c4 = new BasicClientCookie("name", "value2"); c4.setDomain("domain.com"); c4.setPath("/whatever-not"); Assertions.assertNotEquals(0, comparator.compare(c3, c4)); } @Test public void testCookieIdentityComparasionByNameDomainAndNullPath() { final CookieIdentityComparator comparator = CookieIdentityComparator.INSTANCE; final BasicClientCookie c1 = new BasicClientCookie("name", "value1"); c1.setDomain("www.domain.com"); c1.setPath("/"); final BasicClientCookie c2 = new BasicClientCookie("name", "value2"); c2.setDomain("www.domain.com"); c2.setPath(null); Assertions.assertEquals(0, comparator.compare(c1, c2)); final BasicClientCookie c3 = new BasicClientCookie("name", "value1"); c3.setDomain("www.domain.com"); c3.setPath("/whatever"); final BasicClientCookie c4 = new BasicClientCookie("name", "value2"); c4.setDomain("domain.com"); c4.setPath(null); Assertions.assertNotEquals(0, comparator.compare(c3, c4)); } } TestDefaultBackoffStrategy.java000066400000000000000000000061371434266521000413070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.ConnectException; import java.net.SocketTimeoutException; import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestDefaultBackoffStrategy { private DefaultBackoffStrategy impl; @BeforeEach public void setUp() { impl = new DefaultBackoffStrategy(); } @Test public void backsOffForSocketTimeouts() { assertTrue(impl.shouldBackoff(new SocketTimeoutException())); } @Test public void backsOffForConnectionTimeouts() { assertTrue(impl.shouldBackoff(new ConnectException())); } @Test public void doesNotBackOffForConnectionManagerTimeout() { assertFalse(impl.shouldBackoff(new ConnectionRequestTimeoutException())); } @Test public void backsOffForServiceUnavailable() { final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable"); assertTrue(impl.shouldBackoff(resp)); } @Test public void backsOffForTooManyRequests() { final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_TOO_MANY_REQUESTS, "Too Many Requests"); assertTrue(impl.shouldBackoff(resp)); } @Test public void doesNotBackOffForNon429And503StatusCodes() { for(int i = 100; i <= 599; i++) { if (i== HttpStatus.SC_TOO_MANY_REQUESTS || i == HttpStatus.SC_SERVICE_UNAVAILABLE) { continue; } final HttpResponse resp = new BasicHttpResponse(i, "Foo"); assertFalse(impl.shouldBackoff(resp)); } } } TestFutureRequestExecutionService.java000066400000000000000000000172531434266521000427550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import static org.hamcrest.MatcherAssert.assertThat; import java.io.IOException; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.io.HttpClientResponseHandler; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @SuppressWarnings("boxing") // test code public class TestFutureRequestExecutionService { private HttpServer localServer; private String uri; private FutureRequestExecutionService httpAsyncClientWithFuture; private final AtomicBoolean blocked = new AtomicBoolean(false); @BeforeEach public void before() throws Exception { this.localServer = ServerBootstrap.bootstrap() .register("/wait", (request, response, context) -> { try { while(blocked.get()) { Thread.sleep(10); } } catch (final InterruptedException e) { throw new IllegalStateException(e); } response.setCode(200); }).create(); this.localServer.start(); uri = "http://localhost:" + this.localServer.getLocalPort() + "/wait"; final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() .setMaxConnPerRoute(5) .build(); final CloseableHttpClient httpClient = HttpClientBuilder.create() .setConnectionManager(cm) .build(); final ExecutorService executorService = Executors.newFixedThreadPool(5); httpAsyncClientWithFuture = new FutureRequestExecutionService(httpClient, executorService); } @AfterEach public void after() throws Exception { blocked.set(false); // any remaining requests should unblock this.localServer.stop(); httpAsyncClientWithFuture.close(); } @Test public void shouldExecuteSingleCall() throws InterruptedException, ExecutionException { final FutureTask task = httpAsyncClientWithFuture.execute( new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler()); Assertions.assertTrue(task.get(), "request should have returned OK"); } @Test public void shouldCancel() throws InterruptedException, ExecutionException { final FutureTask task = httpAsyncClientWithFuture.execute( new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler()); task.cancel(true); final Exception exception = Assertions.assertThrows(Exception.class, task::get); assertThat(exception, CoreMatchers.anyOf( CoreMatchers.instanceOf(CancellationException.class), CoreMatchers.instanceOf(ExecutionException.class))); } @Test public void shouldTimeout() throws InterruptedException, ExecutionException, TimeoutException { blocked.set(true); final FutureTask task = httpAsyncClientWithFuture.execute( new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler()); Assertions.assertThrows(TimeoutException.class, () -> task.get(10, TimeUnit.MILLISECONDS)); } @Test public void shouldExecuteMultipleCalls() throws Exception { final int reqNo = 100; final Queue> tasks = new LinkedList<>(); for(int i = 0; i < reqNo; i++) { final Future task = httpAsyncClientWithFuture.execute( new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler()); tasks.add(task); } for (final Future task : tasks) { final Boolean b = task.get(); Assertions.assertNotNull(b); Assertions.assertTrue(b, "request should have returned OK"); } } @Test public void shouldExecuteMultipleCallsAndCallback() throws Exception { final int reqNo = 100; final Queue> tasks = new LinkedList<>(); final CountDownLatch latch = new CountDownLatch(reqNo); for(int i = 0; i < reqNo; i++) { final Future task = httpAsyncClientWithFuture.execute( new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler(), new CountingCallback(latch)); tasks.add(task); } Assertions.assertTrue(latch.await(5, TimeUnit.SECONDS)); for (final Future task : tasks) { final Boolean b = task.get(); Assertions.assertNotNull(b); Assertions.assertTrue(b, "request should have returned OK"); } } private final class CountingCallback implements FutureCallback { private final CountDownLatch latch; CountingCallback(final CountDownLatch latch) { super(); this.latch = latch; } @Override public void failed(final Exception ex) { latch.countDown(); } @Override public void completed(final Boolean result) { latch.countDown(); } @Override public void cancelled() { latch.countDown(); } } private final class OkidokiHandler implements HttpClientResponseHandler { @Override public Boolean handleResponse( final ClassicHttpResponse response) throws IOException { return response.getCode() == 200; } } } TestHttpAsyncClientBuilder.java000066400000000000000000000063131434266521000413030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; import org.apache.hc.client5.http.async.AsyncExecChainHandler; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.nio.AsyncEntityProducer; import org.junit.jupiter.api.Test; public class TestHttpAsyncClientBuilder { @Test public void testAddInterceptorFirstDoesNotThrow() throws IOException { HttpAsyncClients.custom() .addExecInterceptorFirst("first", NopExecChainHandler.INSTANCE) .build() .close(); } @Test public void testAddInterceptorLastDoesNotThrow() throws IOException { HttpAsyncClients.custom() .addExecInterceptorLast("last", NopExecChainHandler.INSTANCE) .build() .close(); } @Test public void testH2AddInterceptorFirstDoesNotThrow() throws IOException { HttpAsyncClients.customHttp2() .addExecInterceptorFirst("first", NopExecChainHandler.INSTANCE) .build() .close(); } @Test public void testH2AddInterceptorLastDoesNotThrow() throws IOException { HttpAsyncClients.customHttp2() .addExecInterceptorLast("last", NopExecChainHandler.INSTANCE) .build() .close(); } enum NopExecChainHandler implements AsyncExecChainHandler { INSTANCE; @Override public void execute(final HttpRequest request, final AsyncEntityProducer entityProducer, final AsyncExecChain.Scope scope, final AsyncExecChain chain, final AsyncExecCallback asyncExecCallback) throws HttpException, IOException { chain.proceed(request, entityProducer, scope, asyncExecCallback); } } } TestHttpClientBuilder.java000066400000000000000000000047461434266521000403150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.junit.jupiter.api.Test; public class TestHttpClientBuilder { @Test public void testAddInterceptorFirstDoesNotThrow() throws IOException { // HTTPCLIENT-2083 HttpClients.custom() .addExecInterceptorFirst("first", NopExecChainHandler.INSTANCE) .build() .close(); } @Test public void testAddInterceptorLastDoesNotThrow() throws IOException { // HTTPCLIENT-2083 HttpClients.custom() .addExecInterceptorLast("last", NopExecChainHandler.INSTANCE) .build() .close(); } enum NopExecChainHandler implements ExecChainHandler { INSTANCE; @Override public ClassicHttpResponse execute( final ClassicHttpRequest request, final ExecChain.Scope scope, final ExecChain chain) throws IOException, HttpException { return chain.proceed(request, scope); } } }TestHttpClientBuilderInterceptors.java000066400000000000000000000077551434266521000427220ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.impl.bootstrap.HttpServer; import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.io.CloseMode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @SuppressWarnings("boxing") // test code public class TestHttpClientBuilderInterceptors { private HttpServer localServer; private String uri; private CloseableHttpClient httpClient; @BeforeEach public void before() throws Exception { this.localServer = ServerBootstrap.bootstrap() .register("/test", (request, response, context) -> { final Header testInterceptorHeader = request.getHeader("X-Test-Interceptor"); if (testInterceptorHeader != null) { response.setHeader(testInterceptorHeader); } response.setCode(200); }).create(); this.localServer.start(); uri = "http://localhost:" + this.localServer.getLocalPort() + "/test"; final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() .setMaxConnPerRoute(5) .build(); httpClient = HttpClientBuilder.create() .setConnectionManager(cm) .addExecInterceptorLast("test-interceptor", (request, scope, chain) -> { request.setHeader("X-Test-Interceptor", "active"); return chain.proceed(request, scope); }) .build(); } @AfterEach public void after() throws Exception { this.httpClient.close(CloseMode.IMMEDIATE); this.localServer.stop(); } @Test public void testAddExecInterceptorLastShouldBeExecuted() throws IOException, HttpException { final ClassicHttpRequest request = new HttpPost(uri); final HttpResponse response = httpClient.execute(request, httpResponse -> { EntityUtils.consume(httpResponse.getEntity()); return httpResponse; }); Assertions.assertEquals(200, response.getCode()); final Header testFilterHeader = response.getHeader("X-Test-Interceptor"); Assertions.assertNotNull(testFilterHeader); } } TestHttpRequestRetryExec.java000066400000000000000000000337141434266521000410500ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SuppressWarnings({"boxing","static-access"}) // test code public class TestHttpRequestRetryExec { @Mock private HttpRequestRetryStrategy retryStrategy; @Mock private ExecChain chain; @Mock private ExecRuntime endpoint; @Mock private TimeValue nextInterval; private HttpRequestRetryExec retryExec; private HttpHost target; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); retryExec = new HttpRequestRetryExec(retryStrategy); target = new HttpHost("localhost", 80); } @Test public void testFundamentals1() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(retryStrategy.retryRequest( Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(Boolean.TRUE, Boolean.FALSE); Mockito.when(retryStrategy.getRetryInterval( Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(TimeValue.ZERO_MILLISECONDS); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); retryExec.execute(request, scope, chain); Mockito.verify(chain, Mockito.times(2)).proceed( Mockito.any(), Mockito.same(scope)); Mockito.verify(response, Mockito.times(1)).close(); } @Test public void testRetrySleepOnIOException() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.same(request), Mockito.any())).thenThrow(new IOException("Ka-boom")); Mockito.when(retryStrategy.retryRequest( Mockito.any(), Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(Boolean.TRUE, Boolean.FALSE); Mockito.when(retryStrategy.getRetryInterval( Mockito.any(), Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(nextInterval); Mockito.when(nextInterval.getDuration()).thenReturn(100L); Mockito.when(nextInterval.compareTo(Mockito.any())).thenReturn(-1); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); retryExec.execute(request, scope, chain); Mockito.verify(chain, Mockito.times(2)).proceed( Mockito.any(), Mockito.same(scope)); Mockito.verify(nextInterval, Mockito.times(1)).sleep(); } @Test public void testRetryIntervalGreaterResponseTimeout() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom() .setResponseTimeout(Timeout.ofSeconds(3)) .build()); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(retryStrategy.retryRequest( Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(Boolean.TRUE, Boolean.FALSE); Mockito.when(retryStrategy.getRetryInterval( Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(TimeValue.ofSeconds(5)); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); retryExec.execute(request, scope, chain); Mockito.verify(chain, Mockito.times(1)).proceed( Mockito.any(), Mockito.same(scope)); Mockito.verify(response, Mockito.times(0)).close(); } @Test public void testRetryIntervalResponseTimeoutNull() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom() .setResponseTimeout(null) .build()); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(retryStrategy.retryRequest( Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(Boolean.TRUE, Boolean.FALSE); Mockito.when(retryStrategy.getRetryInterval( Mockito.any(), Mockito.anyInt(), Mockito.any())).thenReturn(TimeValue.ofSeconds(1)); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); retryExec.execute(request, scope, chain); Mockito.verify(chain, Mockito.times(2)).proceed( Mockito.any(), Mockito.same(scope)); Mockito.verify(response, Mockito.times(1)).close(); } @Test public void testStrategyRuntimeException() throws Exception { final HttpRoute route = new HttpRoute(target); final ClassicHttpRequest request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenReturn(response); Mockito.doThrow(new RuntimeException("Ooopsie")).when(retryStrategy).retryRequest( Mockito.any(), Mockito.anyInt(), Mockito.any()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); Assertions.assertThrows(RuntimeException.class, () -> retryExec.execute(request, scope, chain)); Mockito.verify(response).close(); } @Test public void testNonRepeatableEntityResponseReturnedImmediately() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpPost request = new HttpPost("/test"); request.setEntity(EntityBuilder.create() .setStream(new ByteArrayInputStream(new byte[]{})) .build()); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenReturn(response); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); final ClassicHttpResponse finalResponse = retryExec.execute(request, scope, chain); Assertions.assertSame(response, finalResponse); Mockito.verify(response, Mockito.times(0)).close(); } @Test public void testFundamentals2() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet originalRequest = new HttpGet("/test"); originalRequest.addHeader("header", "this"); originalRequest.addHeader("header", "that"); final HttpClientContext context = HttpClientContext.create(); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenAnswer(invocationOnMock -> { final Object[] args = invocationOnMock.getArguments(); final ClassicHttpRequest wrapper = (ClassicHttpRequest) args[0]; final Header[] headers = wrapper.getHeaders(); Assertions.assertEquals(2, headers.length); Assertions.assertEquals("this", headers[0].getValue()); Assertions.assertEquals("that", headers[1].getValue()); wrapper.addHeader("Cookie", "monster"); throw new IOException("Ka-boom"); }); Mockito.when(retryStrategy.retryRequest( Mockito.any(), Mockito.any(), Mockito.eq(1), Mockito.any())).thenReturn(Boolean.TRUE); final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context); final ClassicHttpRequest request = ClassicRequestBuilder.copy(originalRequest).build(); Assertions.assertThrows(IOException.class, () -> retryExec.execute(request, scope, chain)); Mockito.verify(chain, Mockito.times(2)).proceed( Mockito.any(), Mockito.same(scope)); } @Test public void testAbortedRequest() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet originalRequest = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenThrow(new IOException("Ka-boom")); Mockito.when(endpoint.isExecutionAborted()).thenReturn(true); final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context); final ClassicHttpRequest request = ClassicRequestBuilder.copy(originalRequest).build(); Assertions.assertThrows(IOException.class, () -> retryExec.execute(request, scope, chain)); Mockito.verify(chain, Mockito.times(1)).proceed( Mockito.same(request), Mockito.same(scope)); Mockito.verify(retryStrategy, Mockito.never()).retryRequest( Mockito.any(), Mockito.any(), Mockito.anyInt(), Mockito.any()); } @Test public void testNonRepeatableRequest() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpPost originalRequest = new HttpPost("/test"); originalRequest.setEntity(EntityBuilder.create() .setStream(new ByteArrayInputStream(new byte[]{})) .build()); final HttpClientContext context = HttpClientContext.create(); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenAnswer(invocationOnMock -> { final Object[] args = invocationOnMock.getArguments(); final ClassicHttpRequest req = (ClassicHttpRequest) args[0]; req.getEntity().writeTo(new ByteArrayOutputStream()); throw new IOException("Ka-boom"); }); final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context); final ClassicHttpRequest request = ClassicRequestBuilder.copy(originalRequest).build(); Assertions.assertThrows(IOException.class, () -> retryExec.execute(request, scope, chain)); Mockito.verify(chain, Mockito.times(1)).proceed( Mockito.same(request), Mockito.same(scope)); } } TestInternalExecRuntime.java000066400000000000000000000310761434266521000406510ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.slf4j.Logger; @SuppressWarnings({"static-access"}) // test code public class TestInternalExecRuntime { @Mock private Logger log; @Mock private HttpClientConnectionManager mgr; @Mock private LeaseRequest leaseRequest; @Mock private HttpRequestExecutor requestExecutor; @Mock private CancellableDependency cancellableDependency; @Mock private ConnectionEndpoint connectionEndpoint; private HttpRoute route; private InternalExecRuntime execRuntime; @BeforeEach public void setup() { MockitoAnnotations.openMocks(this); route = new HttpRoute(new HttpHost("host", 80)); execRuntime = new InternalExecRuntime(log, mgr, requestExecutor, cancellableDependency); } @Test public void testAcquireEndpoint() throws Exception { final HttpClientContext context = HttpClientContext.create(); @SuppressWarnings("deprecation") final RequestConfig config = RequestConfig.custom() .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS) .setConnectTimeout(123, TimeUnit.MILLISECONDS) .build(); context.setRequestConfig(config); final HttpRoute route = new HttpRoute(new HttpHost("host", 80)); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", route, null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); Assertions.assertSame(connectionEndpoint, execRuntime.ensureValid()); Assertions.assertFalse(execRuntime.isEndpointConnected()); Assertions.assertFalse(execRuntime.isConnectionReusable()); Mockito.verify(leaseRequest).get(Timeout.ofMilliseconds(345)); Mockito.verify(cancellableDependency, Mockito.times(1)).setDependency(leaseRequest); Mockito.verify(cancellableDependency, Mockito.times(1)).setDependency(execRuntime); Mockito.verify(cancellableDependency, Mockito.times(2)).setDependency(Mockito.any()); } @Test public void testAcquireEndpointAlreadyAcquired() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", route, null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); Assertions.assertSame(connectionEndpoint, execRuntime.ensureValid()); Assertions.assertThrows(IllegalStateException.class, () -> execRuntime.acquireEndpoint("some-id", route, null, context)); } @Test public void testAcquireEndpointLeaseRequestTimeout() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenThrow(new TimeoutException("timeout")); Assertions.assertThrows(ConnectionRequestTimeoutException.class, () -> execRuntime.acquireEndpoint("some-id", route, null, context)); } @Test public void testAcquireEndpointLeaseRequestFailure() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenThrow(new ExecutionException(new IllegalStateException())); Assertions.assertThrows(RequestFailedException.class, () -> execRuntime.acquireEndpoint("some-id", route, null, context)); } @Test public void testAbortEndpoint() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", new HttpRoute(new HttpHost("host", 80)), null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); execRuntime.discardEndpoint(); Assertions.assertFalse(execRuntime.isEndpointAcquired()); Mockito.verify(connectionEndpoint).close(CloseMode.IMMEDIATE); Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); execRuntime.discardEndpoint(); Mockito.verify(connectionEndpoint, Mockito.times(1)).close(CloseMode.IMMEDIATE); Mockito.verify(mgr, Mockito.times(1)).release( Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testCancell() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", route, null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); Assertions.assertTrue(execRuntime.cancel()); Assertions.assertFalse(execRuntime.isEndpointAcquired()); Mockito.verify(connectionEndpoint).close(CloseMode.IMMEDIATE); Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); Assertions.assertFalse(execRuntime.cancel()); Mockito.verify(connectionEndpoint, Mockito.times(1)).close(CloseMode.IMMEDIATE); Mockito.verify(mgr, Mockito.times(1)).release( Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testReleaseEndpointReusable() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", route, null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); execRuntime.markConnectionReusable("some state", TimeValue.ofMilliseconds(100000)); execRuntime.releaseEndpoint(); Assertions.assertFalse(execRuntime.isEndpointAcquired()); Mockito.verify(connectionEndpoint, Mockito.never()).close(); Mockito.verify(mgr).release(connectionEndpoint, "some state", TimeValue.ofMilliseconds(100000)); execRuntime.releaseEndpoint(); Mockito.verify(mgr, Mockito.times(1)).release( Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testReleaseEndpointNonReusable() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", route, null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); execRuntime.markConnectionReusable("some state", TimeValue.ofMilliseconds(100000)); execRuntime.markConnectionNonReusable(); execRuntime.releaseEndpoint(); Assertions.assertFalse(execRuntime.isEndpointAcquired()); Mockito.verify(connectionEndpoint, Mockito.times(1)).close(CloseMode.IMMEDIATE); Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS); execRuntime.releaseEndpoint(); Mockito.verify(mgr, Mockito.times(1)).release( Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testConnectEndpoint() throws Exception { final HttpClientContext context = HttpClientContext.create(); @SuppressWarnings("deprecation") final RequestConfig config = RequestConfig.custom() .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS) .setConnectTimeout(123, TimeUnit.MILLISECONDS) .build(); context.setRequestConfig(config); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", route, null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); Mockito.when(connectionEndpoint.isConnected()).thenReturn(false); Assertions.assertFalse(execRuntime.isEndpointConnected()); execRuntime.connectEndpoint(context); Mockito.verify(mgr).connect(connectionEndpoint, Timeout.ofMilliseconds(123), context); } @Test public void testDisonnectEndpoint() throws Exception { final HttpClientContext context = HttpClientContext.create(); Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any())) .thenReturn(leaseRequest); Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint); execRuntime.acquireEndpoint("some-id", route, null, context); Assertions.assertTrue(execRuntime.isEndpointAcquired()); Mockito.when(connectionEndpoint.isConnected()).thenReturn(true); Assertions.assertTrue(execRuntime.isEndpointConnected()); execRuntime.connectEndpoint(context); Mockito.verify(mgr, Mockito.never()).connect( Mockito.same(connectionEndpoint), Mockito.any(), Mockito.any()); execRuntime.disconnectEndpoint(); Mockito.verify(connectionEndpoint).close(); } } TestInternalHttpClient.java000066400000000000000000000227411434266521000404760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.Closeable; import java.io.IOException; import java.util.Arrays; import org.apache.hc.client5.http.ClientProtocolException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** * Simple tests for {@link InternalHttpClient}. */ @SuppressWarnings({"static-access"}) // test code public class TestInternalHttpClient { @Mock private HttpClientConnectionManager connManager; @Mock private HttpRequestExecutor requestExecutor; @Mock private ExecChainHandler execChain; @Mock private HttpRoutePlanner routePlanner; @Mock private Lookup cookieSpecRegistry; @Mock private Lookup authSchemeRegistry; @Mock private CookieStore cookieStore; @Mock private CredentialsProvider credentialsProvider; @Mock private RequestConfig defaultConfig; @Mock private Closeable closeable1; @Mock private Closeable closeable2; private InternalHttpClient client; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); client = new InternalHttpClient(connManager, requestExecutor, new ExecChainElement(execChain, null), routePlanner, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, defaultConfig, Arrays.asList(closeable1, closeable2)); } @Test public void testExecute() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any())).thenReturn(route); Mockito.when(execChain.execute( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn( CloseableHttpResponse.adapt(new BasicClassicHttpResponse(200))); client.execute(httpget, response -> null); Mockito.verify(execChain).execute( Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void testExecuteHttpException() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any())).thenReturn(route); Mockito.when(execChain.execute( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn( CloseableHttpResponse.adapt(new BasicClassicHttpResponse(200))); Mockito.when(execChain.execute( Mockito.any(), Mockito.any(), Mockito.any())).thenThrow(new HttpException()); Assertions.assertThrows(ClientProtocolException.class, () -> client.execute(httpget, response -> null)); } @Test public void testExecuteDefaultContext() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any())).thenReturn(route); Mockito.when(execChain.execute( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn( CloseableHttpResponse.adapt(new BasicClassicHttpResponse(200))); final HttpClientContext context = HttpClientContext.create(); client.execute(httpget, context, response -> null); Assertions.assertSame(cookieSpecRegistry, context.getCookieSpecRegistry()); Assertions.assertSame(authSchemeRegistry, context.getAuthSchemeRegistry()); Assertions.assertSame(cookieStore, context.getCookieStore()); Assertions.assertSame(credentialsProvider, context.getCredentialsProvider()); Assertions.assertSame(defaultConfig, context.getRequestConfig()); } @Test public void testExecuteRequestConfig() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any())).thenReturn(route); Mockito.when(execChain.execute( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn( CloseableHttpResponse.adapt(new BasicClassicHttpResponse(200))); final RequestConfig config = RequestConfig.custom().build(); httpget.setConfig(config); final HttpClientContext context = HttpClientContext.create(); client.execute(httpget, context, response -> null); Assertions.assertSame(config, context.getRequestConfig()); } @Test public void testExecuteLocalContext() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any())).thenReturn(route); Mockito.when(execChain.execute( Mockito.any(), Mockito.any(), Mockito.any())).thenReturn( CloseableHttpResponse.adapt(new BasicClassicHttpResponse(200))); final HttpClientContext context = HttpClientContext.create(); final Lookup localCookieSpecRegistry = Mockito.mock(Lookup.class); final Lookup localAuthSchemeRegistry = Mockito.mock(Lookup.class); final CookieStore localCookieStore = Mockito.mock(CookieStore.class); final CredentialsProvider localCredentialsProvider = Mockito.mock(CredentialsProvider.class); final RequestConfig localConfig = RequestConfig.custom().build(); context.setCookieSpecRegistry(localCookieSpecRegistry); context.setAuthSchemeRegistry(localAuthSchemeRegistry); context.setCookieStore(localCookieStore); context.setCredentialsProvider(localCredentialsProvider); context.setRequestConfig(localConfig); client.execute(httpget, context, response -> null); Assertions.assertSame(localCookieSpecRegistry, context.getCookieSpecRegistry()); Assertions.assertSame(localAuthSchemeRegistry, context.getAuthSchemeRegistry()); Assertions.assertSame(localCookieStore, context.getCookieStore()); Assertions.assertSame(localCredentialsProvider, context.getCredentialsProvider()); Assertions.assertSame(localConfig, context.getRequestConfig()); } @Test public void testClientClose() throws Exception { client.close(); Mockito.verify(closeable1).close(); Mockito.verify(closeable2).close(); } @Test public void testClientCloseIOException() throws Exception { Mockito.doThrow(new IOException()).when(closeable1).close(); client.close(); Mockito.verify(closeable1).close(); Mockito.verify(closeable2).close(); } } TestMainClientExec.java000066400000000000000000000357071434266521000375610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InterruptedIOException; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.UserTokenHandler; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.impl.ConnectionShutdownException; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.apache.hc.core5.http.protocol.HttpProcessor; import org.apache.hc.core5.util.TimeValue; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SuppressWarnings({"boxing","static-access"}) // test code public class TestMainClientExec { @Mock private HttpClientConnectionManager connectionManager; @Mock private HttpProcessor httpProcessor; @Mock private ConnectionReuseStrategy reuseStrategy; @Mock private ConnectionKeepAliveStrategy keepAliveStrategy; @Mock private UserTokenHandler userTokenHandler; @Mock private ExecRuntime execRuntime; private MainClientExec mainClientExec; private HttpHost target; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); mainClientExec = new MainClientExec(connectionManager, httpProcessor, reuseStrategy, keepAliveStrategy, userTokenHandler); target = new HttpHost("foo", 80); } @Test public void testFundamentals() throws Exception { final HttpRoute route = new HttpRoute(target); final ClassicHttpRequest request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); final HttpEntity responseEntity = EntityBuilder.create() .setStream(new ByteArrayInputStream(new byte[]{})) .build(); response.setEntity(responseEntity); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenReturn(response); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); Mockito.verify(httpProcessor).process(request, null, context); Mockito.verify(execRuntime).execute("test", request, context); Mockito.verify(httpProcessor).process(response, responseEntity, context); Assertions.assertEquals(route, context.getHttpRoute()); Assertions.assertSame(request, context.getRequest()); Assertions.assertSame(response, context.getResponse()); } @Test public void testExecRequestNonPersistentConnection() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.setEntity(EntityBuilder.create() .setStream(new ByteArrayInputStream(new byte[]{})) .build()); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), Mockito.same(response), Mockito.any())).thenReturn(false); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); Mockito.verify(execRuntime).execute("test", request, context); Mockito.verify(execRuntime, Mockito.times(1)).markConnectionNonReusable(); Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint(); Assertions.assertNull(context.getUserToken()); Assertions.assertNotNull(finalResponse); Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse); } @Test public void testExecRequestNonPersistentConnectionNoResponseEntity() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); response.setEntity(null); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), Mockito.same(response), Mockito.any())).thenReturn(false); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); Mockito.verify(execRuntime).execute("test", request, context); Mockito.verify(execRuntime).markConnectionNonReusable(); Mockito.verify(execRuntime).releaseEndpoint(); Assertions.assertNotNull(finalResponse); Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse); } @Test public void testExecRequestPersistentConnection() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); // The entity is streaming response.setEntity(EntityBuilder.create() .setStream(new ByteArrayInputStream(new byte[]{})) .build()); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), Mockito.same(response), Mockito.any())).thenReturn(true); Mockito.when(keepAliveStrategy.getKeepAliveDuration( Mockito.same(response), Mockito.any())).thenReturn(TimeValue.ofMilliseconds(678L)); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); Mockito.verify(execRuntime).execute("test", request, context); Mockito.verify(execRuntime).markConnectionReusable(null, TimeValue.ofMilliseconds(678L)); Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint(); Assertions.assertNotNull(finalResponse); Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse); } @Test public void testExecRequestPersistentConnectionNoResponseEntity() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), Mockito.same(response), Mockito.any())).thenReturn(true); Mockito.when(keepAliveStrategy.getKeepAliveDuration( Mockito.same(response), Mockito.any())).thenReturn(TimeValue.ofMilliseconds(678L)); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); Mockito.verify(execRuntime).execute("test", request, context); Mockito.verify(execRuntime).releaseEndpoint(); Assertions.assertNotNull(finalResponse); Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse); } @Test public void testExecRequestConnectionRelease() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); // The entity is streaming response.setEntity(EntityBuilder.create() .setStream(new ByteArrayInputStream(new byte[]{})) .build()); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), Mockito.same(response), Mockito.any())).thenReturn(Boolean.FALSE); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); Mockito.verify(execRuntime, Mockito.times(1)).execute("test", request, context); Mockito.verify(execRuntime, Mockito.never()).disconnectEndpoint(); Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint(); Assertions.assertNotNull(finalResponse); Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse); finalResponse.close(); Mockito.verify(execRuntime).disconnectEndpoint(); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testExecConnectionShutDown() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenThrow(new ConnectionShutdownException()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(InterruptedIOException.class, () -> mainClientExec.execute(request, scope, null)); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testExecRuntimeException() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenThrow(new RuntimeException("Ka-boom")); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(RuntimeException.class, () -> mainClientExec.execute(request, scope, null)); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testExecHttpException() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenThrow(new HttpException("Ka-boom")); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(HttpException.class, () -> mainClientExec.execute(request, scope, null)); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testExecIOException() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://bar/test"); Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), Mockito.any())).thenThrow(new IOException("Ka-boom")); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(IOException.class, () -> mainClientExec.execute(request, scope, null)); Mockito.verify(execRuntime).discardEndpoint(); } } TestNullBackoffStrategy.java000066400000000000000000000037721434266521000406370ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import static org.junit.jupiter.api.Assertions.assertFalse; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestNullBackoffStrategy { private NullBackoffStrategy impl; @BeforeEach public void setUp() { impl = new NullBackoffStrategy(); } @Test public void doesNotBackoffForThrowables() { assertFalse(impl.shouldBackoff(new Exception())); } @Test public void doesNotBackoffForResponses() { final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable"); assertFalse(impl.shouldBackoff(resp)); } } TestProtocolExec.java000066400000000000000000000312331434266521000373250ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.auth.AuthScope; import org.apache.hc.client5.http.auth.ChallengeType; import org.apache.hc.client5.http.auth.StandardAuthScheme; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder; import org.apache.hc.client5.http.impl.auth.NTLMScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; @SuppressWarnings({"static-access"}) // test code public class TestProtocolExec { @Mock private AuthenticationStrategy targetAuthStrategy; @Mock private AuthenticationStrategy proxyAuthStrategy; @Mock private ExecChain chain; @Mock private ExecRuntime execRuntime; private ProtocolExec protocolExec; private HttpHost target; private HttpHost proxy; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); protocolExec = new ProtocolExec(targetAuthStrategy, proxyAuthStrategy, null, true); target = new HttpHost("foo", 80); proxy = new HttpHost("bar", 8888); } @Test public void testUserInfoInRequestURI() throws Exception { final HttpRoute route = new HttpRoute(new HttpHost("somehost", 8080)); final ClassicHttpRequest request = new HttpGet("http://somefella:secret@bar/test"); final HttpClientContext context = HttpClientContext.create(); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(ProtocolException.class, () -> protocolExec.execute(request, scope, chain)); } @Test public void testPostProcessHttpException() throws Exception { final HttpRoute route = new HttpRoute(target); final ClassicHttpRequest request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenReturn(response); Mockito.doThrow(new HttpException("Ooopsie")).when(chain).proceed(Mockito.any(), Mockito.any()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(HttpException.class, () -> protocolExec.execute(request, scope, chain)); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testPostProcessIOException() throws Exception { final HttpRoute route = new HttpRoute(target); final ClassicHttpRequest request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenReturn(response); Mockito.doThrow(new IOException("Ooopsie")).when(chain).proceed(Mockito.any(), Mockito.any()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(IOException.class, () -> protocolExec.execute(request, scope, chain)); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testPostProcessRuntimeException() throws Exception { final HttpRoute route = new HttpRoute(target); final ClassicHttpRequest request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class); Mockito.when(chain.proceed( Mockito.any(), Mockito.any())).thenReturn(response); Mockito.doThrow(new RuntimeException("Ooopsie")).when(chain).proceed(Mockito.any(), Mockito.any()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); Assertions.assertThrows(RuntimeException.class, () -> protocolExec.execute(request, scope, chain)); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testExecRequestRetryOnAuthChallenge() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final ClassicHttpRequest request = new HttpGet("http://foo/test"); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?"); response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test"); final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(inStream1) .build()); final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK"); final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4})); response2.setEntity(EntityBuilder.create() .setStream(inStream2) .build()); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(new AuthScope(target), "user", "pass".toCharArray()) .build()); Mockito.when(chain.proceed( Mockito.same(request), Mockito.any())).thenReturn(response1, response2); Mockito.when(targetAuthStrategy.select( Mockito.eq(ChallengeType.TARGET), Mockito.any(), Mockito.any())).thenReturn(Collections.singletonList(new BasicScheme())); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain); Mockito.verify(chain, Mockito.times(2)).proceed(request, scope); Mockito.verify(inStream1).close(); Mockito.verify(inStream2, Mockito.never()).close(); Assertions.assertNotNull(finalResponse); Assertions.assertEquals(200, finalResponse.getCode()); } @Test public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception { final HttpRoute route = new HttpRoute(target); final ClassicHttpRequest request = new HttpGet("http://foo/test"); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?"); response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test"); final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(inStream1) .build()); final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK"); final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4})); response2.setEntity(EntityBuilder.create() .setStream(inStream2) .build()); final HttpClientContext context = new HttpClientContext(); final AuthExchange authExchange = new AuthExchange(); authExchange.setState(AuthExchange.State.SUCCESS); authExchange.select(new NTLMScheme()); context.setAuthExchange(target, authExchange); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(new AuthScope(target), "user", "pass".toCharArray()) .build()); Mockito.when(chain.proceed( Mockito.same(request), Mockito.any())).thenReturn(response1, response2); Mockito.when(targetAuthStrategy.select( Mockito.eq(ChallengeType.TARGET), Mockito.any(), Mockito.any())).thenReturn(Collections.singletonList(new BasicScheme())); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain); Mockito.verify(chain, Mockito.times(2)).proceed(request, scope); Mockito.verify(execRuntime).disconnectEndpoint(); Mockito.verify(inStream2, Mockito.never()).close(); Assertions.assertNotNull(finalResponse); Assertions.assertEquals(200, finalResponse.getCode()); Assertions.assertNotNull(authExchange.getAuthScheme()); } @Test public void testExecEntityEnclosingRequest() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = new HttpClientContext(); final HttpPost request = new HttpPost("http://foo/test"); final InputStream inStream0 = new ByteArrayInputStream(new byte[] {1, 2, 3}); request.setEntity(EntityBuilder.create() .setStream(inStream0) .build()); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?"); response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test"); final InputStream inStream1 = new ByteArrayInputStream(new byte[] {1, 2, 3}); response1.setEntity(EntityBuilder.create() .setStream(inStream1) .build()); context.setCredentialsProvider(CredentialsProviderBuilder.create() .add(new AuthScope(target), "user", "pass".toCharArray()) .build()); Mockito.when(chain.proceed( Mockito.same(request), Mockito.any())).thenAnswer((Answer) invocationOnMock -> { final Object[] args = invocationOnMock.getArguments(); final ClassicHttpRequest requestEE = (ClassicHttpRequest) args[0]; requestEE.getEntity().writeTo(new ByteArrayOutputStream()); return response1; }); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse response = protocolExec.execute(request, scope, chain); Assertions.assertEquals(401, response.getCode()); } } TestRedirectExec.java000066400000000000000000000435731434266521000372770ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; import org.apache.hc.client5.http.CircularRedirectException; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RedirectException; import org.apache.hc.client5.http.auth.AuthExchange; import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.auth.BasicScheme; import org.apache.hc.client5.http.impl.auth.NTLMScheme; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.RedirectLocations; import org.apache.hc.client5.http.protocol.RedirectStrategy; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestRedirectExec { @Mock private HttpRoutePlanner httpRoutePlanner; @Mock private ExecChain chain; @Mock private ExecRuntime endpoint; private RedirectStrategy redirectStrategy; private RedirectExec redirectExec; private HttpHost target; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); target = new HttpHost("localhost", 80); redirectStrategy = Mockito.spy(new DefaultRedirectStrategy()); redirectExec = new RedirectExec(httpRoutePlanner, redirectStrategy); } @Test public void testFundamentals() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response1 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY)); final URI redirect = new URI("http://localhost:80/redirect"); response1.setHeader(HttpHeaders.LOCATION, redirect.toASCIIString()); final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); final HttpEntity entity1 = EntityBuilder.create() .setStream(inStream1) .build(); response1.setEntity(entity1); final ClassicHttpResponse response2 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_OK)); final InputStream inStream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); final HttpEntity entity2 = EntityBuilder.create() .setStream(inStream2) .build(); response2.setEntity(entity2); Mockito.when(chain.proceed( ArgumentMatchers.same(request), ArgumentMatchers.any())).thenReturn(response1); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(redirect), ArgumentMatchers.any())).thenReturn(response2); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); redirectExec.execute(request, scope, chain); final ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class); Mockito.verify(chain, Mockito.times(2)).proceed(reqCaptor.capture(), ArgumentMatchers.same(scope)); final List allValues = reqCaptor.getAllValues(); Assertions.assertNotNull(allValues); Assertions.assertEquals(2, allValues.size()); Assertions.assertSame(request, allValues.get(0)); Mockito.verify(response1, Mockito.times(1)).close(); Mockito.verify(inStream1, Mockito.times(2)).close(); Mockito.verify(response2, Mockito.never()).close(); Mockito.verify(inStream2, Mockito.never()).close(); } @Test public void testMaxRedirect() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final RequestConfig config = RequestConfig.custom() .setRedirectsEnabled(true) .setMaxRedirects(3) .build(); context.setRequestConfig(config); final ClassicHttpResponse response1 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY)); final URI redirect = new URI("http://localhost:80/redirect"); response1.setHeader(HttpHeaders.LOCATION, redirect.toASCIIString()); Mockito.when(chain.proceed(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(response1); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); Assertions.assertThrows(RedirectException.class, () -> redirectExec.execute(request, scope, chain)); } @Test public void testRelativeRedirect() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response1 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY)); final URI redirect = new URI("/redirect"); response1.setHeader(HttpHeaders.LOCATION, redirect.toASCIIString()); Mockito.when(chain.proceed( ArgumentMatchers.same(request), ArgumentMatchers.any())).thenReturn(response1); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); Assertions.assertThrows(HttpException.class, () -> redirectExec.execute(request, scope, chain)); } @Test public void testCrossSiteRedirect() throws Exception { final HttpHost proxy = new HttpHost("proxy"); final HttpRoute route = new HttpRoute(target, proxy); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final AuthExchange targetAuthExchange = new AuthExchange(); targetAuthExchange.setState(AuthExchange.State.SUCCESS); targetAuthExchange.select(new BasicScheme()); final AuthExchange proxyAuthExchange = new AuthExchange(); proxyAuthExchange.setState(AuthExchange.State.SUCCESS); proxyAuthExchange.select(new NTLMScheme()); context.setAuthExchange(target, targetAuthExchange); context.setAuthExchange(proxy, proxyAuthExchange); final ClassicHttpResponse response1 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY)); final URI redirect = new URI("http://otherhost:8888/redirect"); response1.setHeader(HttpHeaders.LOCATION, redirect.toASCIIString()); final ClassicHttpResponse response2 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_OK)); final HttpHost otherHost = new HttpHost("otherhost", 8888); Mockito.when(chain.proceed( ArgumentMatchers.same(request), ArgumentMatchers.any())).thenReturn(response1); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(redirect), ArgumentMatchers.any())).thenReturn(response2); Mockito.when(httpRoutePlanner.determineRoute( ArgumentMatchers.eq(otherHost), ArgumentMatchers.any())).thenReturn(new HttpRoute(otherHost)); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); redirectExec.execute(request, scope, chain); final AuthExchange authExchange1 = context.getAuthExchange(target); Assertions.assertNotNull(authExchange1); Assertions.assertEquals(AuthExchange.State.UNCHALLENGED, authExchange1.getState()); Assertions.assertNull(authExchange1.getAuthScheme()); final AuthExchange authExchange2 = context.getAuthExchange(proxy); Assertions.assertNotNull(authExchange2); Assertions.assertEquals(AuthExchange.State.UNCHALLENGED, authExchange2.getState()); Assertions.assertNull(authExchange2.getAuthScheme()); } @Test public void testAllowCircularRedirects() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom() .setCircularRedirectsAllowed(true) .build()); final URI uri = URI.create("http://localhost/"); final HttpGet request = new HttpGet(uri); final URI uri1 = URI.create("http://localhost/stuff1"); final URI uri2 = URI.create("http://localhost/stuff2"); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY); response1.addHeader("Location", uri1.toASCIIString()); final ClassicHttpResponse response2 = new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY); response2.addHeader("Location", uri2.toASCIIString()); final ClassicHttpResponse response3 = new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY); response3.addHeader("Location", uri1.toASCIIString()); final ClassicHttpResponse response4 = new BasicClassicHttpResponse(HttpStatus.SC_OK); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(uri), ArgumentMatchers.any())).thenReturn(response1); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(uri1), ArgumentMatchers.any())).thenReturn(response2, response4); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(uri2), ArgumentMatchers.any())).thenReturn(response3); Mockito.when(httpRoutePlanner.determineRoute( ArgumentMatchers.eq(new HttpHost("localhost")), ArgumentMatchers.any())).thenReturn(route); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); redirectExec.execute(request, scope, chain); final RedirectLocations uris = context.getRedirectLocations(); Assertions.assertNotNull(uris); Assertions.assertEquals(Arrays.asList(uri1, uri2, uri1), uris.getAll()); } @Test public void testGetLocationUriDisallowCircularRedirects() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom() .setCircularRedirectsAllowed(false) .build()); final URI uri = URI.create("http://localhost/"); final HttpGet request = new HttpGet(uri); final URI uri1 = URI.create("http://localhost/stuff1"); final URI uri2 = URI.create("http://localhost/stuff2"); final ClassicHttpResponse response1 = new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY); response1.addHeader("Location", uri1.toASCIIString()); final ClassicHttpResponse response2 = new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY); response2.addHeader("Location", uri2.toASCIIString()); final ClassicHttpResponse response3 = new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY); response3.addHeader("Location", uri1.toASCIIString()); Mockito.when(httpRoutePlanner.determineRoute( ArgumentMatchers.eq(new HttpHost("localhost")), ArgumentMatchers.any())).thenReturn(route); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(uri), ArgumentMatchers.any())).thenReturn(response1); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(uri1), ArgumentMatchers.any())).thenReturn(response2); Mockito.when(chain.proceed( HttpRequestMatcher.matchesRequestUri(uri2), ArgumentMatchers.any())).thenReturn(response3); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); Assertions.assertThrows(CircularRedirectException.class, () -> redirectExec.execute(request, scope, chain)); } @Test public void testRedirectRuntimeException() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response1 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY)); final URI redirect = new URI("http://localhost:80/redirect"); response1.setHeader(HttpHeaders.LOCATION, redirect.toASCIIString()); Mockito.when(chain.proceed( ArgumentMatchers.same(request), ArgumentMatchers.any())).thenReturn(response1); Mockito.doThrow(new RuntimeException("Oppsie")).when(redirectStrategy).getLocationURI( ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); Assertions.assertThrows(RuntimeException.class, () -> redirectExec.execute(request, scope, chain)); Mockito.verify(response1).close(); } @Test public void testRedirectProtocolException() throws Exception { final HttpRoute route = new HttpRoute(target); final HttpGet request = new HttpGet("/test"); final HttpClientContext context = HttpClientContext.create(); final ClassicHttpResponse response1 = Mockito.spy(new BasicClassicHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY)); final URI redirect = new URI("http://localhost:80/redirect"); response1.setHeader(HttpHeaders.LOCATION, redirect.toASCIIString()); final InputStream inStream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); final HttpEntity entity1 = EntityBuilder.create() .setStream(inStream1) .build(); response1.setEntity(entity1); Mockito.when(chain.proceed( ArgumentMatchers.same(request), ArgumentMatchers.any())).thenReturn(response1); Mockito.doThrow(new ProtocolException("Oppsie")).when(redirectStrategy).getLocationURI( ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, endpoint, context); Assertions.assertThrows(ProtocolException.class, () -> redirectExec.execute(request, scope, chain)); Mockito.verify(inStream1, Mockito.times(2)).close(); Mockito.verify(response1).close(); } private static class HttpRequestMatcher implements ArgumentMatcher { private final URI expectedRequestUri; HttpRequestMatcher(final URI requestUri) { super(); this.expectedRequestUri = requestUri; } @Override public boolean matches(final ClassicHttpRequest argument) { if (argument == null) { return false; } try { final URI requestUri = argument.getUri(); return this.expectedRequestUri.equals(requestUri); } catch (final URISyntaxException ex) { return false; } } static ClassicHttpRequest matchesRequestUri(final URI requestUri) { return ArgumentMatchers.argThat(new HttpRequestMatcher(requestUri)); } } } TestResponseEntityProxy.java000066400000000000000000000136301434266521000407550ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.List; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.impl.io.ChunkedInputStream; import org.apache.hc.core5.http.impl.io.SessionInputBufferImpl; import org.apache.hc.core5.http.io.SessionInputBuffer; import org.apache.hc.core5.http.io.entity.BasicHttpEntity; import org.apache.hc.core5.http.message.BasicClassicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class TestResponseEntityProxy { @Mock private ClassicHttpResponse response; @Mock private ExecRuntime execRuntime; @Mock private HttpEntity entity; @BeforeEach public void setUp() { MockitoAnnotations.openMocks(this); Mockito.when(entity.isStreaming()).thenReturn(Boolean.TRUE); Mockito.when(response.getEntity()).thenReturn(entity); } @Test public void testGetTrailersWithNoChunkedInputStream() throws Exception { final ByteArrayInputStream inputStream = new ByteArrayInputStream("Test payload".getBytes()); Mockito.when(entity.getContent()).thenReturn(inputStream); final ArgumentCaptor httpEntityArgumentCaptor = ArgumentCaptor.forClass(HttpEntity.class); ResponseEntityProxy.enhance(response, execRuntime); Mockito.verify(response).setEntity(httpEntityArgumentCaptor.capture()); final HttpEntity wrappedEntity = httpEntityArgumentCaptor.getValue(); final InputStream is = wrappedEntity.getContent(); while (is.read() != -1) {} // read until the end final Supplier> trailers = wrappedEntity.getTrailers(); Assertions.assertTrue(trailers.get().isEmpty()); } @Test public void testGetTrailersWithChunkedInputStream() throws Exception { final SessionInputBuffer sessionInputBuffer = new SessionInputBufferImpl(100); final ByteArrayInputStream inputStream = new ByteArrayInputStream("0\r\nX-Test-Trailer-Header: test\r\n".getBytes()); final ChunkedInputStream chunkedInputStream = new ChunkedInputStream(sessionInputBuffer, inputStream); Mockito.when(entity.getContent()).thenReturn(chunkedInputStream); final ArgumentCaptor httpEntityArgumentCaptor = ArgumentCaptor.forClass(HttpEntity.class); ResponseEntityProxy.enhance(response, execRuntime); Mockito.verify(response).setEntity(httpEntityArgumentCaptor.capture()); final HttpEntity wrappedEntity = httpEntityArgumentCaptor.getValue(); final InputStream is = wrappedEntity.getContent(); while (is.read() != -1) {} // consume the stream so it can reach to trailers and parse final Supplier> trailers = wrappedEntity.getTrailers(); final List headers = trailers.get(); Assertions.assertEquals(1, headers.size()); final Header header = headers.get(0); Assertions.assertEquals("X-Test-Trailer-Header", header.getName()); Assertions.assertEquals("test", header.getValue()); } @Test public void testWriteToNullDrainsAndReleasesStream() throws Exception { final SessionInputBuffer sessionInputBuffer = new SessionInputBufferImpl(100); final ByteArrayInputStream inputStream = new ByteArrayInputStream("0\r\nX-Test-Trailer-Header: test\r\n".getBytes()); final ChunkedInputStream chunkedInputStream = new ChunkedInputStream(sessionInputBuffer, inputStream); final CloseableHttpResponse resp = new CloseableHttpResponse(new BasicClassicHttpResponse(200), execRuntime); final HttpEntity entity = new BasicHttpEntity(chunkedInputStream, null, true); Assertions.assertTrue(entity.isStreaming()); resp.setEntity(entity); ResponseEntityProxy.enhance(resp, execRuntime); final HttpEntity wrappedEntity = resp.getEntity(); wrappedEntity.writeTo(null); Mockito.verify(execRuntime).releaseEndpoint(); final Supplier> trailers = wrappedEntity.getTrailers(); final List headers = trailers.get(); Assertions.assertEquals(1, headers.size()); final Header header = headers.get(0); Assertions.assertEquals("X-Test-Trailer-Header", header.getName()); Assertions.assertEquals("test", header.getValue()); } }TestResponseEntityWrapper.java000066400000000000000000000126701434266521000412570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.classic; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.SocketException; import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @SuppressWarnings("boxing") // test code public class TestResponseEntityWrapper { private InputStream inStream; private HttpEntity entity; private ExecRuntime execRuntime; private ResponseEntityProxy wrapper; @BeforeEach public void setup() throws Exception { inStream = Mockito.mock(InputStream.class); entity = Mockito.mock(HttpEntity.class); Mockito.when(entity.getContent()).thenReturn(inStream); execRuntime = Mockito.mock(ExecRuntime.class); wrapper = new ResponseEntityProxy(entity, execRuntime); } @Test public void testReusableEntityStreamClosed() throws Exception { Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); EntityUtils.consume(wrapper); Mockito.verify(inStream, Mockito.times(1)).close(); Mockito.verify(execRuntime).releaseEndpoint(); } @Test public void testReusableEntityStreamClosedIOError() throws Exception { Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); Mockito.doThrow(new IOException()).when(inStream).close(); Assertions.assertThrows(IOException.class, () -> EntityUtils.consume(wrapper)); Mockito.verify(execRuntime, Mockito.atLeast(1)).discardEndpoint(); } @Test public void testEntityStreamClosedIOErrorAlreadyReleased() throws Exception { Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); Mockito.when(execRuntime.isEndpointAcquired()).thenReturn(false); Mockito.doThrow(new SocketException()).when(inStream).close(); EntityUtils.consume(wrapper); Mockito.verify(execRuntime).discardEndpoint(); } @Test public void testReusableEntityWriteTo() throws Exception { final OutputStream outStream = Mockito.mock(OutputStream.class); Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); wrapper.writeTo(outStream); Mockito.verify(execRuntime).releaseEndpoint(); } @Test public void testReusableEntityWriteToIOError() throws Exception { final OutputStream outStream = Mockito.mock(OutputStream.class); Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); Mockito.doThrow(new IOException()).when(entity).writeTo(outStream); Assertions.assertThrows(IOException.class, () -> wrapper.writeTo(outStream)); Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint(); Mockito.verify(execRuntime, Mockito.atLeast(1)).discardEndpoint(); } @Test public void testReusableEntityEndOfStream() throws Exception { Mockito.when(inStream.read()).thenReturn(-1); Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); final InputStream content = wrapper.getContent(); Assertions.assertEquals(-1, content.read()); Mockito.verify(inStream).close(); Mockito.verify(execRuntime).releaseEndpoint(); } @Test public void testReusableEntityEndOfStreamIOError() throws Exception { Mockito.when(inStream.read()).thenReturn(-1); Mockito.when(entity.isStreaming()).thenReturn(true); Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true); Mockito.doThrow(new IOException()).when(inStream).close(); final InputStream content = wrapper.getContent(); Assertions.assertThrows(IOException.class, () -> content.read()); Mockito.verify(execRuntime, Mockito.atLeast(1)).discardEndpoint(); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/000077500000000000000000000000001434266521000331215ustar00rootroot00000000000000TestBasicClientCookie.java000066400000000000000000000072521434266521000400650ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link BasicClientCookie}. */ public class TestBasicClientCookie { @SuppressWarnings("unused") @Test public void testConstructor() { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); Assertions.assertEquals("name", cookie.getName()); Assertions.assertEquals("value", cookie.getValue()); Assertions.assertThrows(NullPointerException.class, () -> new BasicClientCookie(null, null)); } @Test public void testCloning() throws Exception { final BasicClientCookie orig = new BasicClientCookie("name", "value"); orig.setDomain("domain"); orig.setPath("/"); orig.setAttribute("attrib", "stuff"); final BasicClientCookie clone = (BasicClientCookie) orig.clone(); Assertions.assertEquals(orig.getName(), clone.getName()); Assertions.assertEquals(orig.getValue(), clone.getValue()); Assertions.assertEquals(orig.getDomain(), clone.getDomain()); Assertions.assertEquals(orig.getPath(), clone.getPath()); Assertions.assertEquals(orig.getAttribute("attrib"), clone.getAttribute("attrib")); } @Test public void testSerialization() throws Exception { final BasicClientCookie orig = new BasicClientCookie("name", "value"); orig.setDomain("domain"); orig.setPath("/"); orig.setAttribute("attrib", "stuff"); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final BasicClientCookie clone = (BasicClientCookie) inStream.readObject(); Assertions.assertEquals(orig.getName(), clone.getName()); Assertions.assertEquals(orig.getValue(), clone.getValue()); Assertions.assertEquals(orig.getDomain(), clone.getDomain()); Assertions.assertEquals(orig.getPath(), clone.getPath()); Assertions.assertEquals(orig.getAttribute("attrib"), clone.getAttribute("attrib")); } } TestBasicCookieAttribHandlers.java000066400000000000000000000430661434266521000415600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.time.Instant; import java.util.Arrays; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieAttributeHandler; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.psl.DomainType; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.apache.hc.client5.http.utils.DateUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestBasicCookieAttribHandlers { @Test public void testBasicDomainParse() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; h.parse(cookie, "www.somedomain.com"); Assertions.assertEquals("www.somedomain.com", cookie.getDomain()); } @Test public void testBasicDomainParseInvalid1() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "")); } @Test public void testBasicDomainParseInvalid2() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, null)); } @Test public void testBasicDomainValidate1() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("www.somedomain.com", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain(".somedomain.com"); h.validate(cookie, origin); cookie.setDomain(".otherdomain.com"); Assertions.assertThrows(MalformedCookieException.class, () -> h.validate(cookie, origin)); cookie.setDomain("www.otherdomain.com"); Assertions.assertThrows(MalformedCookieException.class, () -> h.validate(cookie, origin)); } @Test public void testBasicDomainValidate2() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain("somehost"); h.validate(cookie, origin); cookie.setDomain("otherhost"); Assertions.assertThrows(MalformedCookieException.class, () -> h.validate(cookie, origin)); } @Test public void testBasicDomainValidate3() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somedomain.com", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain(".somedomain.com"); h.validate(cookie, origin); } @Test public void testBasicDomainValidate4() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somedomain.com", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain(null); Assertions.assertThrows(MalformedCookieException.class, () -> h.validate(cookie, origin)); } @Test public void testBasicDomainMatch1() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somedomain.com", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain("somedomain.com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "somedomain.com"); Assertions.assertTrue(h.match(cookie, origin)); cookie.setDomain(".somedomain.com"); Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicDomainMatch2() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("www.somedomain.com", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain("somedomain.com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "somedomain.com"); Assertions.assertTrue(h.match(cookie, origin)); cookie.setDomain(".somedomain.com"); Assertions.assertTrue(h.match(cookie, origin)); cookie.setDomain(null); Assertions.assertFalse(h.match(cookie, origin)); } @Test public void testBasicDomainMatchOneLetterPrefix() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("a.somedomain.com", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain("somedomain.com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "somedomain.com"); Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicDomainMatchMixedCase() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("a.SomeDomain.com", 80, "/", false); final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; cookie.setDomain("somedoMain.Com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "somedoMain.Com"); Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicDomainInvalidInput() throws Exception { final CookieAttributeHandler h = BasicDomainHandler.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> h.parse(null, null)); Assertions.assertThrows(NullPointerException.class, () -> h.validate(null, null)); Assertions.assertThrows(NullPointerException.class, () -> h.validate(new BasicClientCookie("name", "value"), null)); Assertions.assertThrows(NullPointerException.class, () -> h.match(null, null)); Assertions.assertThrows(NullPointerException.class, () -> h.match(new BasicClientCookie("name", "value"), null)); } @Test public void testBasicPathParse() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; h.parse(cookie, "stuff"); Assertions.assertEquals("stuff", cookie.getPath()); h.parse(cookie, ""); Assertions.assertEquals("/", cookie.getPath()); h.parse(cookie, null); Assertions.assertEquals("/", cookie.getPath()); } @Test public void testBasicPathMatch1() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/stuff", false); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; cookie.setPath("/stuff"); Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicPathMatch2() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/stuff/", false); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; cookie.setPath("/stuff"); Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicPathMatch3() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/stuff/more-stuff", false); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; cookie.setPath("/stuff"); Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicPathMatch4() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/stuffed", false); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; cookie.setPath("/stuff"); Assertions.assertFalse(h.match(cookie, origin)); } @Test public void testBasicPathMatch5() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/otherstuff", false); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; cookie.setPath("/stuff"); Assertions.assertFalse(h.match(cookie, origin)); } @Test public void testBasicPathMatch6() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/stuff", false); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; cookie.setPath("/stuff/"); Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicPathMatch7() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieOrigin origin = new CookieOrigin("somehost", 80, "/stuff", false); final CookieAttributeHandler h = BasicPathHandler.INSTANCE; Assertions.assertTrue(h.match(cookie, origin)); } @Test public void testBasicPathInvalidInput() throws Exception { final CookieAttributeHandler h = BasicPathHandler.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> h.parse(null, null)); Assertions.assertThrows(NullPointerException.class, () -> h.match(null, null)); Assertions.assertThrows(NullPointerException.class, () -> h.match(new BasicClientCookie("name", "value"), null)); } @Test public void testBasicMaxAgeParse() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicMaxAgeHandler.INSTANCE; h.parse(cookie, "2000"); Assertions.assertNotNull(cookie.getExpiryInstant()); } @Test public void testBasicMaxAgeParseInvalid() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicMaxAgeHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "garbage")); Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, null)); } @Test public void testBasicMaxAgeInvalidInput() throws Exception { final CookieAttributeHandler h = BasicMaxAgeHandler.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> h.parse(null, null)); } @Test public void testBasicSecureParse() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicSecureHandler.INSTANCE; h.parse(cookie, "whatever"); Assertions.assertTrue(cookie.isSecure()); h.parse(cookie, null); Assertions.assertTrue(cookie.isSecure()); } @Test public void testBasicSecureMatch() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = BasicSecureHandler.INSTANCE; final CookieOrigin origin1 = new CookieOrigin("somehost", 80, "/stuff", false); cookie.setSecure(false); Assertions.assertTrue(h.match(cookie, origin1)); cookie.setSecure(true); Assertions.assertFalse(h.match(cookie, origin1)); final CookieOrigin origin2 = new CookieOrigin("somehost", 80, "/stuff", true); cookie.setSecure(false); Assertions.assertTrue(h.match(cookie, origin2)); cookie.setSecure(true); Assertions.assertTrue(h.match(cookie, origin2)); } @Test public void testBasicSecureInvalidInput() throws Exception { final CookieAttributeHandler h = new BasicSecureHandler(); Assertions.assertThrows(NullPointerException.class, () -> h.parse(null, null)); Assertions.assertThrows(NullPointerException.class, () -> h.match(null, null)); Assertions.assertThrows(NullPointerException.class, () -> h.match(new BasicClientCookie("name", "value"), null)); } @Test public void testBasicExpiresParse() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = new BasicExpiresHandler(DateUtils.FORMATTER_RFC1123); h.parse(cookie, DateUtils.formatStandardDate(Instant.now())); Assertions.assertNotNull(cookie.getExpiryInstant()); } @Test public void testBasicExpiresParseInvalid() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = new BasicExpiresHandler(DateUtils.FORMATTER_RFC1123); Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "garbage")); Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, null)); } @SuppressWarnings("unused") @Test public void testBasicExpiresInvalidInput() throws Exception { final CookieAttributeHandler h = new BasicExpiresHandler(DateUtils.FORMATTER_RFC1123); Assertions.assertThrows(NullPointerException.class, () -> h.parse(null, null)); } @Test public void testPublicSuffixFilter() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final PublicSuffixMatcher matcher = new PublicSuffixMatcher(DomainType.ICANN, Arrays.asList("co.uk", "com"), null); final PublicSuffixDomainFilter h = new PublicSuffixDomainFilter(BasicDomainHandler.INSTANCE, matcher); cookie.setDomain(".co.uk"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".co.uk"); Assertions.assertFalse(h.match(cookie, new CookieOrigin("apache.co.uk", 80, "/stuff", false))); cookie.setDomain("co.uk"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "co.uk"); Assertions.assertFalse(h.match(cookie, new CookieOrigin("apache.co.uk", 80, "/stuff", false))); cookie.setDomain(".co.com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".co.com"); Assertions.assertTrue(h.match(cookie, new CookieOrigin("apache.co.com", 80, "/stuff", false))); cookie.setDomain("co.com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "co.com"); Assertions.assertTrue(h.match(cookie, new CookieOrigin("apache.co.com", 80, "/stuff", false))); cookie.setDomain(".com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".com"); Assertions.assertFalse(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false))); cookie.setDomain("com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "com"); Assertions.assertFalse(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false))); cookie.setDomain("apache.com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "apache.com"); Assertions.assertTrue(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false))); cookie.setDomain(".apache.com"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".apache.com"); Assertions.assertTrue(h.match(cookie, new CookieOrigin("www.apache.com", 80, "/stuff", false))); cookie.setDomain("localhost"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "localhost"); Assertions.assertTrue(h.match(cookie, new CookieOrigin("localhost", 80, "/stuff", false))); } @Test public void testBasicHttpOnlyParse() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = new BasicHttpOnlyHandler(); h.parse(cookie, "true"); Assertions.assertTrue(cookie.isHttpOnly()); h.parse(cookie, "anyone"); Assertions.assertTrue(cookie.isHttpOnly()); } } TestBasicCookieStore.java000066400000000000000000000102251434266521000377350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.Cookie; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link BasicCookieStore}. */ public class TestBasicCookieStore { @Test public void testBasics() throws Exception { final BasicCookieStore store = new BasicCookieStore(); store.addCookie(new BasicClientCookie("name1", "value1")); store.addCookies(new BasicClientCookie[] {new BasicClientCookie("name2", "value2")}); List list = store.getCookies(); Assertions.assertNotNull(list); Assertions.assertEquals(2, list.size()); Assertions.assertEquals("name1", list.get(0).getName()); Assertions.assertEquals("name2", list.get(1).getName()); store.clear(); list = store.getCookies(); Assertions.assertNotNull(list); Assertions.assertEquals(0, list.size()); } @Test public void testExpiredCookie() throws Exception { final BasicCookieStore store = new BasicCookieStore(); final BasicClientCookie cookie = new BasicClientCookie("name1", "value1"); final Instant minus_10_days = Instant.now().minus(10, ChronoUnit.DAYS); cookie.setExpiryDate(minus_10_days); store.addCookie(cookie); final List list = store.getCookies(); Assertions.assertNotNull(list); Assertions.assertEquals(0, list.size()); } @Test public void testSerialization() throws Exception { final BasicCookieStore orig = new BasicCookieStore(); orig.addCookie(new BasicClientCookie("name1", "value1")); orig.addCookie(new BasicClientCookie("name2", "value2")); final ByteArrayOutputStream outbuffer = new ByteArrayOutputStream(); final ObjectOutputStream outStream = new ObjectOutputStream(outbuffer); outStream.writeObject(orig); outStream.close(); final byte[] raw = outbuffer.toByteArray(); final ByteArrayInputStream inBuffer = new ByteArrayInputStream(raw); final ObjectInputStream inStream = new ObjectInputStream(inBuffer); final BasicCookieStore clone = (BasicCookieStore) inStream.readObject(); final List expected = orig.getCookies(); final List clones = clone.getCookies(); Assertions.assertNotNull(expected); Assertions.assertNotNull(clones); Assertions.assertEquals(expected.size(), clones.size()); for (int i = 0; i < expected.size(); i++) { Assertions.assertEquals(expected.get(i).getName(), clones.get(i).getName()); Assertions.assertEquals(expected.get(i).getValue(), clones.get(i).getValue()); } } } TestLaxCookieAttribHandlers.java000066400000000000000000000454631434266521000412660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.time.LocalDateTime; import java.time.temporal.ChronoField; import org.apache.hc.client5.http.cookie.CookieAttributeHandler; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.client5.http.utils.DateUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestLaxCookieAttribHandlers { @Test public void testParseMaxAge() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxMaxAgeHandler.INSTANCE; h.parse(cookie, "2000"); Assertions.assertNotNull(cookie.getExpiryInstant()); } @Test public void testParseMaxNegative() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxMaxAgeHandler.INSTANCE; h.parse(cookie, "-2000"); Assertions.assertNotNull(cookie.getExpiryInstant()); } @Test public void testParseMaxZero() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxMaxAgeHandler.INSTANCE; h.parse(cookie, "0000"); Assertions.assertNotNull(cookie.getExpiryInstant()); } @Test public void testBasicMaxAgeParseEmpty() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxMaxAgeHandler.INSTANCE; h.parse(cookie, " "); Assertions.assertNull(cookie.getExpiryInstant()); } @Test public void testBasicMaxAgeParseInvalid() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxMaxAgeHandler.INSTANCE; h.parse(cookie, "garbage"); Assertions.assertNull(cookie.getExpiryInstant()); } @Test public void testBasicMaxAgeInvalidInput() throws Exception { final CookieAttributeHandler h = LaxMaxAgeHandler.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> h.parse(null, "stuff")); } @Test public void testExpiryGarbage() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, ";;blah,blah;yada ")); } @Test public void testParseExpiry() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "1:0:12 8-jan-2012"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2012, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(1, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(1, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(12, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryInstant() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "1:0:12 8-jan-2012"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2012, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(1, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(1, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(12, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryInvalidTime0() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, null); Assertions.assertNull(cookie.getExpiryInstant()); } @Test public void testParseExpiryInvalidTime1() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "1:0:122 8 dec 1980")); } @Test public void testParseExpiryInvalidTime2() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "24:00:00 8 dec 1980")); } @Test public void testParseExpiryInvalidTime3() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "23:60:00 8 dec 1980")); } @Test public void testParseExpiryInvalidTime4() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "23:00:60 8 dec 1980")); } @Test public void testParseExpiryFunnyTime() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "1:59:00blah; 8-feb-2000"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2000, expiryDate.get(ChronoField.YEAR_OF_ERA)); Assertions.assertEquals(2, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(1, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(0, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryFunnyTimeInstant() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "1:59:00blah; 8-feb-2000"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2000, expiryDate.get(ChronoField.YEAR_OF_ERA)); Assertions.assertEquals(2, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(1, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(0, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryInvalidDayOfMonth1() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "12:00:00 888 mar 1880")); } @Test public void testParseExpiryInvalidDayOfMonth2() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "12:00:00 0 mar 1880")); } @Test public void testParseExpiryInvalidDayOfMonth3() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "12:00:00 32 mar 1880")); } @Test public void testParseExpiryFunnyDayOfMonth() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "12:00:00 8blah;mar;1880"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(1880, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(3, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(12, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(0, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryFunnyDayOfMonthInstant() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "12:00:00 8blah;mar;1880"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(1880, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(3, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(12, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(0, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryInvalidMonth() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "1:00:00 8 dek 80")); } @Test public void testParseExpiryFunnyMonth() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "23:59:59; 1-ApriLLLLL-2008"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2008, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(4, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(1, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(23, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(59, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryFunnyMonthInstant() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "23:59:59; 1-ApriLLLLL-2008"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2008, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(4, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(1, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(23, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(59, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryInvalidYearTooShort() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "1:00:00 8 dec 8")); } @Test public void testParseExpiryInvalidYearTooLong() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "1:00:00 8 dec 88888")); } @Test public void testParseExpiryInvalidYearTooLongAgo() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; Assertions.assertThrows(MalformedCookieException.class, () -> h.parse(cookie, "1:00:00 8 dec 1600")); } @Test public void testParseExpiryFunnyYear() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "23:59:59; 1-Apr-2008blah"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2008, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(4, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(1, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(23, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(59, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryFunnyYearInstant() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "23:59:59; 1-Apr-2008blah"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2008, expiryDate.get(ChronoField.YEAR)); Assertions.assertEquals(4, expiryDate.get(ChronoField.MONTH_OF_YEAR)); Assertions.assertEquals(1, expiryDate.get(ChronoField.DAY_OF_MONTH)); Assertions.assertEquals(23, expiryDate.get(ChronoField.HOUR_OF_DAY)); Assertions.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR)); Assertions.assertEquals(59, expiryDate.get(ChronoField.SECOND_OF_MINUTE)); Assertions.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND)); } @Test public void testParseExpiryYearTwoDigit1() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "23:59:59; 1-Apr-70"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(1970, expiryDate.get(ChronoField.YEAR)); } @Test public void testParseExpiryYearTwoDigit2() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "23:59:59; 1-Apr-99"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(1999, expiryDate.get(ChronoField.YEAR)); } @Test public void testParseExpiryYearTwoDigit3() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); final CookieAttributeHandler h = LaxExpiresHandler.INSTANCE; h.parse(cookie, "23:59:59; 1-Apr-00"); final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryInstant()); Assertions.assertNotNull(expiryDate); Assertions.assertEquals(2000, expiryDate.get(ChronoField.YEAR)); } } TestPublicSuffixListParser.java000066400000000000000000000134051434266521000411640ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.psl.PublicSuffixList; import org.apache.hc.client5.http.psl.PublicSuffixListParser; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestPublicSuffixListParser { private static final String SOURCE_FILE = "suffixlist.txt"; private PublicSuffixDomainFilter filter; @BeforeEach public void setUp() throws Exception { final ClassLoader classLoader = getClass().getClassLoader(); final InputStream in = classLoader.getResourceAsStream(SOURCE_FILE); Assertions.assertNotNull(in); final PublicSuffixList suffixList; try { final PublicSuffixListParser parser = PublicSuffixListParser.INSTANCE; suffixList = parser.parse(new InputStreamReader(in, StandardCharsets.UTF_8)); } finally { in.close(); } final PublicSuffixMatcher matcher = new PublicSuffixMatcher(suffixList.getRules(), suffixList.getExceptions()); this.filter = new PublicSuffixDomainFilter(BasicDomainHandler.INSTANCE, matcher); } @Test public void testParse() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".jp"); cookie.setDomain(".jp"); Assertions.assertFalse(filter.match(cookie, new CookieOrigin("apache.jp", 80, "/stuff", false))); cookie.setDomain(".ac.jp"); Assertions.assertFalse(filter.match(cookie, new CookieOrigin("apache.ac.jp", 80, "/stuff", false))); cookie.setDomain(".any.tokyo.jp"); Assertions.assertFalse(filter.match(cookie, new CookieOrigin("apache.any.tokyo.jp", 80, "/stuff", false))); // exception cookie.setDomain(".metro.tokyo.jp"); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("apache.metro.tokyo.jp", 80, "/stuff", false))); } @Test public void testParseLocal() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); cookie.setDomain("localhost"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "localhost"); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("localhost", 80, "/stuff", false))); cookie.setDomain("somehost"); cookie.setAttribute(Cookie.DOMAIN_ATTR, "somehost"); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("somehost", 80, "/stuff", false))); cookie.setDomain(".localdomain"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".localdomain"); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("somehost.localdomain", 80, "/stuff", false))); cookie.setDomain(".local."); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".local."); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("somehost.local.", 80, "/stuff", false))); cookie.setDomain(".localhost."); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".localhost."); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("somehost.localhost.", 80, "/stuff", false))); cookie.setDomain(".local"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".local"); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("somehost.local", 80, "/stuff", false))); cookie.setDomain(".blah"); cookie.setAttribute(Cookie.DOMAIN_ATTR, ".blah"); Assertions.assertTrue(filter.match(cookie, new CookieOrigin("somehost.blah", 80, "/stuff", false))); } @Test public void testUnicode() throws Exception { final BasicClientCookie cookie = new BasicClientCookie("name", "value"); cookie.setDomain(".h\u00E5.no"); // \u00E5 is Assertions.assertFalse(filter.match(cookie, new CookieOrigin("apache.h\u00E5.no", 80, "/stuff", false))); cookie.setDomain(".xn--h-2fa.no"); Assertions.assertFalse(filter.match(cookie, new CookieOrigin("apache.xn--h-2fa.no", 80, "/stuff", false))); cookie.setDomain(".h\u00E5.no"); Assertions.assertFalse(filter.match(cookie, new CookieOrigin("apache.xn--h-2fa.no", 80, "/stuff", false))); cookie.setDomain(".xn--h-2fa.no"); Assertions.assertFalse(filter.match(cookie, new CookieOrigin("apache.h\u00E5.no", 80, "/stuff", false))); } } TestRFC6265CookieSpec.java000066400000000000000000000357501434266521000374610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/cookie/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.cookie; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.MalformedCookieException; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestRFC6265CookieSpec { @Test public void testParseCookieBasics() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("that"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final Header header = new BasicHeader("Set-Cookie", "name = value ; this = stuff;"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(1, cookies.size()); final Cookie cookie = cookies.get(0); Assertions.assertEquals("name", cookie.getName()); Assertions.assertEquals("value", cookie.getValue()); Assertions.assertEquals("/path", cookie.getPath()); Assertions.assertEquals("host", cookie.getDomain()); Assertions.assertEquals("stuff", cookie.getAttribute("this")); Assertions.assertNull(cookie.getAttribute("that")); Mockito.verify(h1).parse(ArgumentMatchers.any(), ArgumentMatchers.eq("stuff")); Mockito.verify(h2, Mockito.never()).parse(ArgumentMatchers.any(), ArgumentMatchers.anyString()); } @Test public void testParseCookieQuotedValue() throws Exception { final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final Header header = new BasicHeader("Set-Cookie", "name = \" one, two, three; four \" ; this = stuff;"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(1, cookies.size()); final Cookie cookie = cookies.get(0); Assertions.assertEquals("name", cookie.getName()); Assertions.assertEquals(" one, two, three; four ", cookie.getValue()); Assertions.assertEquals("stuff", cookie.getAttribute("this")); } @Test public void testParseCookieWrongHeader() throws Exception { final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final Header header = new BasicHeader("Set-Cookie2", "blah"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); Assertions.assertThrows(MalformedCookieException.class, () -> cookiespec.parse(header, origin)); } @Test public void testParseCookieMissingName() throws Exception { final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final Header header = new BasicHeader("Set-Cookie", "=blah ; this = stuff;"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(0, cookies.size()); } @Test public void testParseCookieMissingValue1() throws Exception { final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final Header header = new BasicHeader("Set-Cookie", "blah"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(0, cookies.size()); } @Test public void testParseCookieMissingValue2() throws Exception { final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final Header header = new BasicHeader("Set-Cookie", "blah;"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); Assertions.assertThrows(MalformedCookieException.class, () -> cookiespec.parse(header, origin)); } @Test public void testParseCookieEmptyValue() throws Exception { final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final Header header = new BasicHeader("Set-Cookie", "blah=;"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(1, cookies.size()); final Cookie cookie = cookies.get(0); Assertions.assertEquals("blah", cookie.getName()); Assertions.assertEquals("", cookie.getValue()); } @Test public void testParseCookieWithAttributes() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("that"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 = v ; p2 = v,0; p3 ; p4"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(1, cookies.size()); final Cookie cookie = cookies.get(0); Assertions.assertEquals("name", cookie.getName()); Assertions.assertEquals("value", cookie.getValue()); Assertions.assertEquals("v", cookie.getAttribute("p1")); Assertions.assertEquals("v,0", cookie.getAttribute("p2")); Assertions.assertTrue(cookie.containsAttribute("p3")); Assertions.assertTrue(cookie.containsAttribute("p4")); Assertions.assertFalse(cookie.containsAttribute("p5")); } @Test public void testParseCookieWithAttributes2() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("that"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 = v"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(1, cookies.size()); final Cookie cookie = cookies.get(0); Assertions.assertEquals("name", cookie.getName()); Assertions.assertEquals("value", cookie.getValue()); Assertions.assertEquals("v", cookie.getAttribute("p1")); } @Test public void testParseCookieWithAttributes3() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("that"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final Header header = new BasicHeader("Set-Cookie", "name = value ; p1 ="); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final List cookies = cookiespec.parse(header, origin); Assertions.assertEquals(1, cookies.size()); final Cookie cookie = cookies.get(0); Assertions.assertEquals("name", cookie.getName()); Assertions.assertEquals("value", cookie.getValue()); Assertions.assertEquals("", cookie.getAttribute("p1")); } @Test public void testValidateCookieBasics() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("that"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final BasicClientCookie cookie = new BasicClientCookie("name", "value"); cookiespec.validate(cookie, origin); Mockito.verify(h1).validate(cookie, origin); Mockito.verify(h2).validate(cookie, origin); } @Test public void testMatchCookie() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("that"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final BasicClientCookie cookie = new BasicClientCookie("name", "value"); Mockito.when(h1.match(cookie, origin)).thenReturn(true); Mockito.when(h2.match(cookie, origin)).thenReturn(true); Assertions.assertTrue(cookiespec.match(cookie, origin)); Mockito.verify(h1).match(cookie, origin); Mockito.verify(h2).match(cookie, origin); } @Test public void testMatchCookieNoMatch() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("that"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); final BasicClientCookie cookie = new BasicClientCookie("name", "value"); Mockito.when(h1.match(cookie, origin)).thenReturn(false); Mockito.when(h2.match(cookie, origin)).thenReturn(false); Assertions.assertFalse(cookiespec.match(cookie, origin)); Mockito.verify(h1).match(cookie, origin); Mockito.verify(h2, Mockito.never()).match(cookie, origin); } @Test public void testFormatCookiesBasics() throws Exception { final Cookie cookie1 = new BasicClientCookie("name1", "value"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final List
headers = cookiespec.formatCookies(Collections.singletonList(cookie1)); Assertions.assertNotNull(headers); Assertions.assertEquals(1, headers.size()); final Header header = headers.get(0); Assertions.assertEquals("Cookie", header.getName()); Assertions.assertEquals("name1=value", header.getValue()); } @Test public void testFormatCookiesIllegalCharsInValue() throws Exception { final Cookie cookie1 = new BasicClientCookie("name1", "value"); final Cookie cookie2 = new BasicClientCookie("name2", "some value"); final Cookie cookie3 = new BasicClientCookie("name3", "\"\\\""); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(); final List
headers = cookiespec.formatCookies(Arrays.asList(cookie1, cookie2, cookie3)); Assertions.assertNotNull(headers); Assertions.assertEquals(1, headers.size()); final Header header = headers.get(0); Assertions.assertEquals("Cookie", header.getName()); Assertions.assertEquals("name1=value; name2=\"some value\"; name3=\"\\\"\\\\\\\"\"", header.getValue()); } @Test public void testParseCookieMultipleAttributes() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("this"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1); final Header header = new BasicHeader("Set-Cookie", "name = value ; this = stuff; this = morestuff;"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); cookiespec.parse(header, origin); Mockito.verify(h1).parse(ArgumentMatchers.any(), ArgumentMatchers.eq("morestuff")); Mockito.verify(h1, Mockito.times(1)).parse(ArgumentMatchers.any(), ArgumentMatchers.anyString()); } @Test public void testParseCookieMaxAgeOverExpires() throws Exception { final CommonCookieAttributeHandler h1 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h1.getAttributeName()).thenReturn("Expires"); final CommonCookieAttributeHandler h2 = Mockito.mock(CommonCookieAttributeHandler.class); Mockito.when(h2.getAttributeName()).thenReturn("Max-Age"); final RFC6265CookieSpec cookiespec = new RFC6265CookieSpec(h1, h2); final Header header = new BasicHeader("Set-Cookie", "name = value ; expires = stuff; max-age = otherstuff;"); final CookieOrigin origin = new CookieOrigin("host", 80, "/path/", true); cookiespec.parse(header, origin); Mockito.verify(h1, Mockito.never()).parse(ArgumentMatchers.any(), ArgumentMatchers.anyString()); Mockito.verify(h2).parse(ArgumentMatchers.any(), ArgumentMatchers.eq("otherstuff")); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/000077500000000000000000000000001434266521000322575ustar00rootroot00000000000000TestBasicHttpClientConnectionManager.java000066400000000000000000000472021434266521000422430ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.io.HttpConnectionFactory; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SuppressWarnings({"boxing","static-access"}) // test code public class TestBasicHttpClientConnectionManager { @Mock private ManagedHttpClientConnection conn; @Mock private HttpConnectionFactory connFactory; @Mock private Lookup socketFactoryRegistry; @Mock private ConnectionSocketFactory plainSocketFactory; @Mock private LayeredConnectionSocketFactory sslSocketFactory; @Mock private Socket socket; @Mock private SchemePortResolver schemePortResolver; @Mock private DnsResolver dnsResolver; private BasicHttpClientConnectionManager mgr; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); mgr = new BasicHttpClientConnectionManager( socketFactoryRegistry, connFactory, schemePortResolver, dnsResolver); } @Test public void testLeaseReleaseNonReusable() throws Exception { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Assertions.assertFalse(endpoint1.isConnected()); mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100)); Assertions.assertNull(mgr.getRoute()); Assertions.assertNull(mgr.getState()); final LeaseRequest connRequest2 = mgr.lease("some-id", route, null); final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(conn2); Assertions.assertFalse(conn2.isConnected()); Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any()); } @Test public void testLeaseReleaseReusable() throws Exception { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100)); Assertions.assertEquals(route, mgr.getRoute()); Assertions.assertNull(mgr.getState()); final LeaseRequest connRequest2 = mgr.lease("some-id", route, null); final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(conn2); Assertions.assertTrue(conn2.isConnected()); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); } @Test public void testLeaseReleaseReusableWithState() throws Exception { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, "some state"); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); mgr.release(endpoint1, "some other state", TimeValue.ofMilliseconds(10000)); Assertions.assertEquals(route, mgr.getRoute()); Assertions.assertEquals("some other state", mgr.getState()); final LeaseRequest connRequest2 = mgr.lease("some-id", route, "some other state"); final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(conn2); Assertions.assertTrue(conn2.isConnected()); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); } @Test public void testLeaseDifferentRoute() throws Exception { final HttpHost target1 = new HttpHost("somehost", 80); final HttpRoute route1 = new HttpRoute(target1); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route1, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND); Assertions.assertEquals(route1, mgr.getRoute()); Assertions.assertNull(mgr.getState()); final HttpHost target2 = new HttpHost("otherhost", 80); final HttpRoute route2 = new HttpRoute(target2); final LeaseRequest connRequest2 = mgr.lease("some-id", route2, null); final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(conn2); Assertions.assertFalse(conn2.isConnected()); Mockito.verify(conn).close(CloseMode.GRACEFUL); Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any()); } @Test public void testLeaseExpired() throws Exception { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); mgr.release(endpoint1, null, TimeValue.ofMilliseconds(10)); Assertions.assertEquals(route, mgr.getRoute()); Assertions.assertNull(mgr.getState()); Thread.sleep(50); final LeaseRequest connRequest2 = mgr.lease("some-id", route, null); final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(conn2); Assertions.assertFalse(conn2.isConnected()); Mockito.verify(conn).close(CloseMode.GRACEFUL); Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any()); } @Test public void testReleaseInvalidArg() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> mgr.release(null, null, TimeValue.NEG_ONE_MILLISECOND)); } @Test public void testReleaseAnotherConnection() throws Exception { final ConnectionEndpoint wrongCon = Mockito.mock(ConnectionEndpoint.class); Assertions.assertThrows(IllegalStateException.class, () -> mgr.release(wrongCon, null, TimeValue.NEG_ONE_MILLISECOND)); } @Test public void testShutdown() throws Exception { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE); mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND); mgr.close(); Mockito.verify(conn, Mockito.times(1)).close(CloseMode.GRACEFUL); final LeaseRequest connRequest2 = mgr.lease("some-id", route, null); Assertions.assertThrows(IllegalStateException.class, () -> connRequest2.get(Timeout.ZERO_MILLISECONDS)); // Should have no effect mgr.closeExpired(); mgr.closeIdle(TimeValue.ZERO_MILLISECONDS); mgr.close(); Mockito.verify(conn, Mockito.times(1)).close(CloseMode.GRACEFUL); } @Test public void testCloseExpired() throws Exception { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); mgr.release(endpoint1, null, TimeValue.ofMilliseconds(10)); Assertions.assertEquals(route, mgr.getRoute()); Assertions.assertNull(mgr.getState()); Thread.sleep(50); mgr.closeExpired(); Mockito.verify(conn).close(CloseMode.GRACEFUL); } @Test public void testCloseIdle() throws Exception { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any()); Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE); mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND); Assertions.assertEquals(route, mgr.getRoute()); Assertions.assertNull(mgr.getState()); Thread.sleep(100); mgr.closeIdle(TimeValue.ofMilliseconds(50)); Mockito.verify(conn).close(CloseMode.GRACEFUL); } @Test public void testAlreadyLeased() throws Exception { final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100)); mgr.getConnection(route, null); Assertions.assertThrows(IllegalStateException.class, () -> mgr.getConnection(route, null)); } @Test public void testTargetConnect() throws Exception { final HttpHost target = new HttpHost("https", "somehost", 443); final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); final HttpRoute route = new HttpRoute(target, local, true); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); mgr.setSocketConfig(sconfig); final ConnectionConfig connectionConfig = ConnectionConfig.custom() .setConnectTimeout(234, TimeUnit.MILLISECONDS) .build(); mgr.setConnectionConfig(connectionConfig); final TlsConfig tlsConfig = TlsConfig.custom() .setHandshakeTimeout(345, TimeUnit.MILLISECONDS) .build(); mgr.setTlsConfig(tlsConfig); Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] {remote}); Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443); Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(plainSocketFactory); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.eq(socket), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(socket); mgr.connect(endpoint1, null, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context); Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket( socket, target, new InetSocketAddress(remote, 8443), new InetSocketAddress(local, 0), Timeout.ofMilliseconds(234), tlsConfig, context); mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context); Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost"); Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target); Mockito.verify(plainSocketFactory, Mockito.times(2)).createSocket(context); Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket( socket, target, new InetSocketAddress(remote, 8443), new InetSocketAddress(local, 0), Timeout.ofMilliseconds(123), tlsConfig, context); } @Test public void testProxyConnectAndUpgrade() throws Exception { final HttpHost target = new HttpHost("https", "somehost", 443); final HttpHost proxy = new HttpHost("someproxy", 8080); final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); final HttpRoute route = new HttpRoute(target, local, proxy, true); Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS); Assertions.assertNotNull(endpoint1); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); mgr.setSocketConfig(sconfig); final ConnectionConfig connectionConfig = ConnectionConfig.custom() .setConnectTimeout(234, TimeUnit.MILLISECONDS) .build(); mgr.setConnectionConfig(connectionConfig); final TlsConfig tlsConfig = TlsConfig.custom() .setHandshakeTimeout(345, TimeUnit.MILLISECONDS) .build(); mgr.setTlsConfig(tlsConfig); Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote}); Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080); Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.eq(socket), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(socket); mgr.connect(endpoint1, null, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy); Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context); Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket( socket, proxy, new InetSocketAddress(remote, 8080), new InetSocketAddress(local, 0), Timeout.ofMilliseconds(234), tlsConfig, context); Mockito.when(conn.getSocket()).thenReturn(socket); mgr.upgrade(endpoint1, context); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); Mockito.verify(sslSocketFactory, Mockito.times(1)).createLayeredSocket( socket, "somehost", 8443, tlsConfig, context); } } TestHttpClientConnectionOperator.java000066400000000000000000000332501434266521000415200ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.concurrent.TimeUnit; import org.apache.hc.client5.http.ConnectTimeoutException; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpHostConnectException; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.UnsupportedSchemeException; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @SuppressWarnings({"boxing","static-access"}) // test code public class TestHttpClientConnectionOperator { private ManagedHttpClientConnection conn; private Socket socket; private ConnectionSocketFactory plainSocketFactory; private LayeredConnectionSocketFactory sslSocketFactory; private Lookup socketFactoryRegistry; private SchemePortResolver schemePortResolver; private DnsResolver dnsResolver; private DefaultHttpClientConnectionOperator connectionOperator; @BeforeEach public void setup() throws Exception { conn = Mockito.mock(ManagedHttpClientConnection.class); socket = Mockito.mock(Socket.class); plainSocketFactory = Mockito.mock(ConnectionSocketFactory.class); sslSocketFactory = Mockito.mock(LayeredConnectionSocketFactory.class); socketFactoryRegistry = Mockito.mock(Lookup.class); schemePortResolver = Mockito.mock(SchemePortResolver.class); dnsResolver = Mockito.mock(DnsResolver.class); connectionOperator = new DefaultHttpClientConnectionOperator( socketFactoryRegistry, schemePortResolver, dnsResolver); } @Test public void testConnect() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("somehost"); final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0}); final InetAddress ip1 = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); final InetAddress ip2 = InetAddress.getByAddress(new byte[] {127, 0, 0, 2}); Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Mockito.when(schemePortResolver.resolve(host)).thenReturn(80); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(socket); final SocketConfig socketConfig = SocketConfig.custom() .setSoKeepAlive(true) .setSoReuseAddress(true) .setSoTimeout(5000, TimeUnit.MILLISECONDS) .setTcpNoDelay(true) .setSoLinger(50, TimeUnit.MILLISECONDS) .build(); final TlsConfig tlsConfig = TlsConfig.custom() .build(); final InetSocketAddress localAddress = new InetSocketAddress(local, 0); connectionOperator.connect(conn, host, localAddress, Timeout.ofMilliseconds(123), socketConfig, tlsConfig, context); Mockito.verify(socket).setKeepAlive(true); Mockito.verify(socket).setReuseAddress(true); Mockito.verify(socket).setSoTimeout(5000); Mockito.verify(socket).setSoLinger(true, 50); Mockito.verify(socket).setTcpNoDelay(true); Mockito.verify(plainSocketFactory).connectSocket( socket, host, new InetSocketAddress(ip1, 80), localAddress, Timeout.ofMilliseconds(123), tlsConfig, context); Mockito.verify(conn, Mockito.times(2)).bind(socket); } @Test public void testConnectTimeout() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("somehost"); final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2}); Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Mockito.when(schemePortResolver.resolve(host)).thenReturn(80); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenThrow(new SocketTimeoutException()); Assertions.assertThrows(ConnectTimeoutException.class, () -> connectionOperator.connect( conn, host, null, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context)); } @Test public void testConnectFailure() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("somehost"); final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2}); Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Mockito.when(schemePortResolver.resolve(host)).thenReturn(80); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenThrow(new ConnectException()); Assertions.assertThrows(HttpHostConnectException.class, () -> connectionOperator.connect( conn, host, null, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context)); } @Test public void testConnectFailover() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("somehost"); final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0}); final InetAddress ip1 = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); final InetAddress ip2 = InetAddress.getByAddress(new byte[] {10, 0, 0, 2}); Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] { ip1, ip2 }); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Mockito.when(schemePortResolver.resolve(host)).thenReturn(80); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.any(), Mockito.any(), Mockito.eq(new InetSocketAddress(ip1, 80)), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenThrow(new ConnectException()); Mockito.when(plainSocketFactory.connectSocket( Mockito.any(), Mockito.any(), Mockito.eq(new InetSocketAddress(ip2, 80)), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(socket); final InetSocketAddress localAddress = new InetSocketAddress(local, 0); final TlsConfig tlsConfig = TlsConfig.custom() .build(); connectionOperator.connect(conn, host, localAddress, Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context); Mockito.verify(plainSocketFactory).connectSocket( socket, host, new InetSocketAddress(ip2, 80), localAddress, Timeout.ofMilliseconds(123), tlsConfig, context); Mockito.verify(conn, Mockito.times(3)).bind(socket); } @Test public void testConnectExplicitAddress() throws Exception { final HttpContext context = new BasicHttpContext(); final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 0}); final InetAddress ip = InetAddress.getByAddress(new byte[] {127, 0, 0, 23}); final HttpHost host = new HttpHost(ip); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Mockito.when(schemePortResolver.resolve(host)).thenReturn(80); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(socket); final InetSocketAddress localAddress = new InetSocketAddress(local, 0); final TlsConfig tlsConfig = TlsConfig.custom() .build(); connectionOperator.connect(conn, host, localAddress, Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context); Mockito.verify(plainSocketFactory).connectSocket( socket, host, new InetSocketAddress(ip, 80), localAddress, Timeout.ofMilliseconds(123), tlsConfig, context); Mockito.verify(dnsResolver, Mockito.never()).resolve(Mockito.anyString()); Mockito.verify(conn, Mockito.times(2)).bind(socket); } @Test public void testUpgrade() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("https", "somehost", -1); Mockito.when(conn.isOpen()).thenReturn(true); Mockito.when(conn.getSocket()).thenReturn(socket); Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory); Mockito.when(schemePortResolver.resolve(host)).thenReturn(443); Mockito.when(sslSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(sslSocketFactory.createLayeredSocket( Mockito.any(), Mockito.eq("somehost"), Mockito.eq(443), Mockito.eq(Timeout.ofMilliseconds(345)), Mockito.any())).thenReturn(socket); connectionOperator.upgrade(conn, host, Timeout.ofMilliseconds(345), context); Mockito.verify(conn).bind(socket); } @Test public void testUpgradeUpsupportedScheme() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("httpsssss", "somehost", -1); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Assertions.assertThrows(UnsupportedSchemeException.class, () -> connectionOperator.upgrade(conn, host, context)); } @Test public void testUpgradeNonLayeringScheme() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpHost host = new HttpHost("http", "somehost", -1); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory); Assertions.assertThrows(UnsupportedSchemeException.class, () -> connectionOperator.upgrade(conn, host, context)); } } TestPoolingHttpClientConnectionManager.java000066400000000000000000000375311434266521000426350ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.io; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.hc.client5.http.DnsResolver; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.TlsConfig; import org.apache.hc.client5.http.io.ConnectionEndpoint; import org.apache.hc.client5.http.io.LeaseRequest; import org.apache.hc.client5.http.io.ManagedHttpClientConnection; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.socket.ConnectionSocketFactory; import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.io.SocketConfig; import org.apache.hc.core5.pool.PoolEntry; import org.apache.hc.core5.pool.StrictConnPool; import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.Timeout; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** * {@link PoolingHttpClientConnectionManager} tests. */ @SuppressWarnings({"boxing","static-access","resource"}) // test code public class TestPoolingHttpClientConnectionManager { @Mock private ManagedHttpClientConnection conn; @Mock private Lookup socketFactoryRegistry; @Mock private ConnectionSocketFactory plainSocketFactory; @Mock private ConnectionSocketFactory sslSocketFactory; @Mock private Socket socket; @Mock private SchemePortResolver schemePortResolver; @Mock private DnsResolver dnsResolver; @Mock private Future> future; @Mock private StrictConnPool pool; private PoolingHttpClientConnectionManager mgr; @BeforeEach public void setup() throws Exception { MockitoAnnotations.openMocks(this); mgr = new PoolingHttpClientConnectionManager( new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver), pool, null); } @Test public void testLeaseRelease() throws Exception { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); final PoolEntry entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND); entry.assignConnection(conn); Mockito.when(conn.isOpen()).thenReturn(true); Mockito.when(conn.isConsistent()).thenReturn(true); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease( Mockito.eq(route), Mockito.eq(null), Mockito.any(), Mockito.eq(null))) .thenReturn(future); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1)); Assertions.assertNotNull(endpoint1); Assertions.assertNotSame(conn, endpoint1); mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND); Mockito.verify(pool).release(entry, true); } @Test public void testReleaseRouteIncomplete() throws Exception { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); final PoolEntry entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease( Mockito.eq(route), Mockito.eq(null), Mockito.any(), Mockito.eq(null))) .thenReturn(future); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1)); Assertions.assertNotNull(endpoint1); Assertions.assertNotSame(conn, endpoint1); mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND); Mockito.verify(pool).release(entry, false); } @Test public void testLeaseFutureTimeout() throws Exception { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenThrow(new TimeoutException()); Mockito.when(pool.lease( Mockito.eq(route), Mockito.eq(null), Mockito.any(), Mockito.eq(null))) .thenReturn(future); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); Assertions.assertThrows(TimeoutException.class, () -> connRequest1.get(Timeout.ofSeconds(1))); } @Test public void testReleaseReusable() throws Exception { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); final PoolEntry entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND); entry.assignConnection(conn); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease( Mockito.eq(route), Mockito.eq(null), Mockito.any(), Mockito.eq(null))) .thenReturn(future); Mockito.when(conn.isOpen()).thenReturn(true); Mockito.when(conn.isConsistent()).thenReturn(true); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1)); Assertions.assertNotNull(endpoint1); Assertions.assertTrue(endpoint1.isConnected()); mgr.release(endpoint1, "some state", TimeValue.NEG_ONE_MILLISECOND); Mockito.verify(pool).release(entry, true); Assertions.assertEquals("some state", entry.getState()); } @Test public void testReleaseNonReusable() throws Exception { final HttpHost target = new HttpHost("localhost", 80); final HttpRoute route = new HttpRoute(target); final PoolEntry entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND); entry.assignConnection(conn); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease( Mockito.eq(route), Mockito.eq(null), Mockito.any(), Mockito.eq(null))) .thenReturn(future); Mockito.when(conn.isOpen()).thenReturn(Boolean.FALSE); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1)); Assertions.assertNotNull(endpoint1); Assertions.assertFalse(endpoint1.isConnected()); mgr.release(endpoint1, "some state", TimeValue.NEG_ONE_MILLISECOND); Mockito.verify(pool).release(entry, false); Assertions.assertNull(entry.getState()); } @Test public void testTargetConnect() throws Exception { final HttpHost target = new HttpHost("https", "somehost", 443); final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); final InetAddress local = InetAddress.getByAddress(new byte[]{127, 0, 0, 1}); final HttpRoute route = new HttpRoute(target, local, true); final PoolEntry entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND); entry.assignConnection(conn); Mockito.when(conn.isOpen()).thenReturn(false); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease( Mockito.eq(route), Mockito.eq(null), Mockito.any(), Mockito.eq(null))) .thenReturn(future); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1)); Assertions.assertNotNull(endpoint1); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); mgr.setDefaultSocketConfig(sconfig); final ConnectionConfig connectionConfig = ConnectionConfig.custom() .setConnectTimeout(234, TimeUnit.MILLISECONDS) .build(); mgr.setDefaultConnectionConfig(connectionConfig); final TlsConfig tlsConfig = TlsConfig.custom() .setHandshakeTimeout(345, TimeUnit.MILLISECONDS) .build(); mgr.setDefaultTlsConfig(tlsConfig); Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[]{remote}); Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443); Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(plainSocketFactory); Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket); Mockito.when(plainSocketFactory.connectSocket( Mockito.eq(socket), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(socket); mgr.connect(endpoint1, null, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context); Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket( socket, target, new InetSocketAddress(remote, 8443), new InetSocketAddress(local, 0), Timeout.ofMilliseconds(234), tlsConfig, context); mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context); Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost"); Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target); Mockito.verify(plainSocketFactory, Mockito.times(2)).createSocket(context); Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket( socket, target, new InetSocketAddress(remote, 8443), new InetSocketAddress(local, 0), Timeout.ofMilliseconds(123), tlsConfig, context); } @Test public void testProxyConnectAndUpgrade() throws Exception { final HttpHost target = new HttpHost("https", "somehost", 443); final HttpHost proxy = new HttpHost("someproxy", 8080); final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1}); final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); final HttpRoute route = new HttpRoute(target, local, proxy, true); final PoolEntry entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND); entry.assignConnection(conn); Mockito.when(conn.isOpen()).thenReturn(false); Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry); Mockito.when(pool.lease( Mockito.eq(route), Mockito.eq(null), Mockito.any(), Mockito.eq(null))) .thenReturn(future); final LeaseRequest connRequest1 = mgr.lease("some-id", route, null); final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1)); Assertions.assertNotNull(endpoint1); final ConnectionSocketFactory plainsf = Mockito.mock(ConnectionSocketFactory.class); final LayeredConnectionSocketFactory sslsf = Mockito.mock(LayeredConnectionSocketFactory.class); final Socket mockSock = Mockito.mock(Socket.class); final HttpClientContext context = HttpClientContext.create(); final SocketConfig sconfig = SocketConfig.custom().build(); mgr.setDefaultSocketConfig(sconfig); final ConnectionConfig connectionConfig = ConnectionConfig.custom() .setConnectTimeout(234, TimeUnit.MILLISECONDS) .build(); mgr.setDefaultConnectionConfig(connectionConfig); final TlsConfig tlsConfig = TlsConfig.custom() .setHandshakeTimeout(345, TimeUnit.MILLISECONDS) .build(); mgr.setDefaultTlsConfig(tlsConfig); Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote}); Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080); Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443); Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainsf); Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslsf); Mockito.when(plainsf.createSocket(Mockito.any())).thenReturn(mockSock); Mockito.when(plainsf.connectSocket( Mockito.eq(mockSock), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(mockSock); mgr.connect(endpoint1, null, context); Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy"); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy); Mockito.verify(plainsf, Mockito.times(1)).createSocket(context); Mockito.verify(plainsf, Mockito.times(1)).connectSocket( mockSock, proxy, new InetSocketAddress(remote, 8080), new InetSocketAddress(local, 0), Timeout.ofMilliseconds(234), tlsConfig, context); Mockito.when(conn.isOpen()).thenReturn(true); Mockito.when(conn.getSocket()).thenReturn(mockSock); mgr.upgrade(endpoint1, context); Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target); Mockito.verify(sslsf, Mockito.times(1)).createLayeredSocket( mockSock, "somehost", 8443, tlsConfig, context); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/000077500000000000000000000000001434266521000333375ustar00rootroot00000000000000TestDefaultProxyRoutePlanner.java000066400000000000000000000066711434266521000417620ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** * Tests for {@link DefaultProxyRoutePlanner}. */ public class TestDefaultProxyRoutePlanner { private HttpHost defaultProxy; private SchemePortResolver schemePortResolver; private DefaultProxyRoutePlanner routePlanner; @BeforeEach public void setup() { defaultProxy = new HttpHost("default.proxy.host", 8888); schemePortResolver = Mockito.mock(SchemePortResolver.class); routePlanner = new DefaultProxyRoutePlanner(defaultProxy, schemePortResolver); } @Test public void testDefaultProxyDirect() throws Exception { final HttpHost target = new HttpHost("http", "somehost", 80); final HttpContext context = new BasicHttpContext(); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(target, route.getTargetHost()); Assertions.assertEquals(defaultProxy, route.getProxyHost()); Assertions.assertEquals(2, route.getHopCount()); Assertions.assertFalse(route.isSecure()); } @Test @SuppressWarnings("deprecation") public void testViaProxy() throws Exception { final HttpHost target = new HttpHost("http", "somehost", 80); final HttpHost proxy = new HttpHost("custom.proxy.host", 8080); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom().setProxy(proxy).build()); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(target, route.getTargetHost()); Assertions.assertEquals(proxy, route.getProxyHost()); Assertions.assertEquals(2, route.getHopCount()); Assertions.assertFalse(route.isSecure()); } } TestDefaultRoutePlanner.java000066400000000000000000000104201434266521000407030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** * Tests for {@link DefaultRoutePlanner}. */ @SuppressWarnings({"boxing","static-access"}) // test code public class TestDefaultRoutePlanner { private SchemePortResolver schemePortResolver; private DefaultRoutePlanner routePlanner; @BeforeEach public void setup() { schemePortResolver = Mockito.mock(SchemePortResolver.class); routePlanner = new DefaultRoutePlanner(schemePortResolver); } @Test public void testDirect() throws Exception { final HttpHost target = new HttpHost("http", "somehost", 80); final HttpContext context = new BasicHttpContext(); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(target, route.getTargetHost()); Assertions.assertEquals(1, route.getHopCount()); Assertions.assertFalse(route.isSecure()); Mockito.verify(schemePortResolver, Mockito.never()).resolve(Mockito.any()); } @Test public void testDirectDefaultPort() throws Exception { final HttpHost target = new HttpHost("https", "somehost", -1); Mockito.when(schemePortResolver.resolve(target)).thenReturn(443); final HttpContext context = new BasicHttpContext(); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(new HttpHost("https", "somehost", 443), route.getTargetHost()); Assertions.assertEquals(1, route.getHopCount()); Assertions.assertTrue(route.isSecure()); } @Test @SuppressWarnings("deprecation") public void testViaProxy() throws Exception { final HttpHost target = new HttpHost("http", "somehost", 80); final HttpHost proxy = new HttpHost("proxy", 8080); final HttpClientContext context = HttpClientContext.create(); context.setRequestConfig(RequestConfig.custom().setProxy(proxy).build()); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(target, route.getTargetHost()); Assertions.assertEquals(proxy, route.getProxyHost()); Assertions.assertEquals(2, route.getHopCount()); Assertions.assertFalse(route.isSecure()); Mockito.verify(schemePortResolver, Mockito.never()).resolve(Mockito.any()); } @Test public void testNullTarget() throws Exception { final HttpContext context = new BasicHttpContext(); Assertions.assertThrows(ProtocolException.class, () -> routePlanner.determineRoute(null, context)); } } TestRouteDirector.java000066400000000000000000000445001434266521000375600ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import java.net.InetAddress; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo.LayerType; import org.apache.hc.client5.http.RouteInfo.TunnelType; import org.apache.hc.client5.http.routing.HttpRouteDirector; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link BasicRouteDirector}. */ public class TestRouteDirector { // a selection of constants for generating routes public final static HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80); public final static HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080); // It is not necessary to have extra targets for https. // The 'layered' and 'secure' flags are specified explicitly // for routes, they will not be determined from the scheme. public final static HttpHost PROXY1 = new HttpHost("proxy1.test.invalid", 80); public final static HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080); public final static HttpHost PROXY3 = new HttpHost("proxy3.test.invalid", 88); public final static InetAddress LOCAL41; public final static InetAddress LOCAL42; public final static InetAddress LOCAL61; public final static InetAddress LOCAL62; // need static initializer to deal with exceptions static { try { LOCAL41 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 }); LOCAL42 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 2 }); LOCAL61 = InetAddress.getByAddress(new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }); LOCAL62 = InetAddress.getByAddress(new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }); } catch (final Exception x) { throw new ExceptionInInitializerError(x); } } @Test public void testIllegal() { final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE; final HttpRoute route = new HttpRoute(TARGET1); Assertions.assertThrows(NullPointerException.class, () -> rowdy.nextStep(null, route)); } @Test public void testDirect() { final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE; final HttpRoute route1 = new HttpRoute(TARGET1); final HttpRoute route2 = new HttpRoute(TARGET2); final HttpRoute route1p1 = new HttpRoute(TARGET1, null, PROXY1, false); int step = rowdy.nextStep(route1, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1"); step = rowdy.nextStep(route2, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route2"); step = rowdy.nextStep(route1, route1); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1 not detected"); step = rowdy.nextStep(route2, route2); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route2 not detected"); step = rowdy.nextStep(route1, route2); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable target not detected"); step = rowdy.nextStep(route1, route1p1); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "invalid proxy not detected"); } @Test public void testProxy() { final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE; final HttpRoute route1p1 = new HttpRoute(TARGET1, null, PROXY1, false); final HttpRoute route1p2 = new HttpRoute(TARGET1, null, PROXY2, false); final HttpRoute route2p1 = new HttpRoute(TARGET2, null, PROXY1, false); final HttpRoute route0 = new HttpRoute(PROXY1); final HttpRoute route1 = new HttpRoute(TARGET1); int step = rowdy.nextStep(route1p1, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1p1"); step = rowdy.nextStep(route1p2, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1p2"); step = rowdy.nextStep(route1p1, route1p1); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1p1 not detected"); step = rowdy.nextStep(route1p2, route1p2); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1p2 not detected"); step = rowdy.nextStep(route2p1, route2p1); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route2p1 not detected"); step = rowdy.nextStep(route1p1, route1p2); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route1p2 not detected"); step = rowdy.nextStep(route1p1, route2p1); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route2p1 not detected"); step = rowdy.nextStep(route1p1, route0); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route0 not detected"); step = rowdy.nextStep(route1p1, route1); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1p1 via route1 not detected"); } @Test public void testProxyChain() { final HttpHost[] chainA = { PROXY1 }; final HttpHost[] chainB = { PROXY1, PROXY2 }; final HttpHost[] chainC = { PROXY2, PROXY1 }; final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE; final HttpRoute route1cA = new HttpRoute(TARGET1, null, chainA, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route1cB = new HttpRoute(TARGET1, null, chainB, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route1cC = new HttpRoute(TARGET1, null, chainC, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route1cD = new HttpRoute(TARGET1, null, chainC, false, TunnelType.PLAIN, LayerType.PLAIN); int step = rowdy.nextStep(route1cA, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cA"); step = rowdy.nextStep(route1cB, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cB"); step = rowdy.nextStep(route1cC, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cC"); step = rowdy.nextStep(route1cD, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1cD"); step = rowdy.nextStep(route1cB, route1cA); Assertions.assertEquals(HttpRouteDirector.TUNNEL_PROXY, step, "wrong step to route 1cB from 1cA"); step = rowdy.nextStep(route1cB, route1cB); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route 1cB not detected"); step = rowdy.nextStep(route1cB, route1cC); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cB from 1cC not detected"); step = rowdy.nextStep(route1cB, route1cD); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cB from 1cD not detected"); step = rowdy.nextStep(route1cA, route1cB); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1cA from 1cB not detected"); } @Test public void testLocalDirect() { final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE; final HttpRoute route1l41 = new HttpRoute(TARGET1, LOCAL41, false); final HttpRoute route1l42 = new HttpRoute(TARGET1, LOCAL42, false); final HttpRoute route1l61 = new HttpRoute(TARGET1, LOCAL61, false); final HttpRoute route1l00 = new HttpRoute(TARGET1, null, false); int step = rowdy.nextStep(route1l41, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l41"); step = rowdy.nextStep(route1l42, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l42"); step = rowdy.nextStep(route1l61, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l61"); step = rowdy.nextStep(route1l00, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1l00"); step = rowdy.nextStep(route1l41, route1l41); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l41 not detected"); step = rowdy.nextStep(route1l42, route1l42); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l42 not detected"); step = rowdy.nextStep(route1l61, route1l61); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l61 not detected"); step = rowdy.nextStep(route1l00, route1l00); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 not detected"); step = rowdy.nextStep(route1l41, route1l42); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l42 not detected"); step = rowdy.nextStep(route1l41, route1l61); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l61 not detected"); step = rowdy.nextStep(route1l41, route1l00); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1l41 via route1l00 not detected"); step = rowdy.nextStep(route1l00, route1l41); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l41 not detected"); step = rowdy.nextStep(route1l00, route1l42); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l42 not detected"); step = rowdy.nextStep(route1l00, route1l61); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1l00 as route1l61 not detected"); } @Test public void testDirectSecure() { final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE; final HttpRoute route1u = new HttpRoute(TARGET1, null, false); final HttpRoute route1s = new HttpRoute(TARGET1, null, true); final HttpRoute route1p1u = new HttpRoute(TARGET1, null, PROXY1, false); final HttpRoute route1p1s = new HttpRoute(TARGET1, null, PROXY1, true); int step = rowdy.nextStep(route1u, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1u"); step = rowdy.nextStep(route1s, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_TARGET, step, "wrong step to route1s"); // unrequested security is currently not tolerated step = rowdy.nextStep(route1u, route1s); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1u from 1s not detected"); // secure layering of direct connections is currently not supported step = rowdy.nextStep(route1s, route1u); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1u not detected"); step = rowdy.nextStep(route1s, route1p1u); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1p1u not detected"); step = rowdy.nextStep(route1s, route1p1s); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route 1s from 1p1s not detected"); } @Test public void testProxyTLS() { final HttpRouteDirector rowdy = BasicRouteDirector.INSTANCE; final HttpRoute route1 = new HttpRoute (TARGET1, null, PROXY1, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route1t = new HttpRoute (TARGET1, null, PROXY1, false, TunnelType.TUNNELLED, LayerType.PLAIN); final HttpRoute route1tl = new HttpRoute (TARGET1, null, PROXY1, false, TunnelType.TUNNELLED, LayerType.LAYERED); final HttpRoute route1s = new HttpRoute (TARGET1, null, PROXY1, true, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route1ts = new HttpRoute (TARGET1, null, PROXY1, true, TunnelType.TUNNELLED, LayerType.PLAIN); final HttpRoute route1tls = new HttpRoute (TARGET1, null, PROXY1, true, TunnelType.TUNNELLED, LayerType.LAYERED); // we don't consider a route that is layered but not tunnelled int step = rowdy.nextStep(route1, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1"); step = rowdy.nextStep(route1t, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1t"); step = rowdy.nextStep(route1tl, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1tl"); step = rowdy.nextStep(route1s, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1s"); step = rowdy.nextStep(route1ts, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1ts"); step = rowdy.nextStep(route1tls, null); Assertions.assertEquals(HttpRouteDirector.CONNECT_PROXY, step, "wrong step to route1tls"); step = rowdy.nextStep(route1, route1); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1 not detected"); step = rowdy.nextStep(route1t, route1t); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1t not detected"); step = rowdy.nextStep(route1tl, route1tl); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1tl not detected"); step = rowdy.nextStep(route1s, route1s); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1s not detected"); step = rowdy.nextStep(route1ts, route1ts); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1ts not detected"); step = rowdy.nextStep(route1tls, route1tls); Assertions.assertEquals(HttpRouteDirector.COMPLETE, step, "complete route1tls not detected"); step = rowdy.nextStep(route1, route1t); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1t not detected"); step = rowdy.nextStep(route1, route1tl); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1tl not detected"); // unrequested security is currently not tolerated step = rowdy.nextStep(route1, route1s); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1s not detected"); step = rowdy.nextStep(route1, route1ts); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1ts not detected"); step = rowdy.nextStep(route1, route1tls); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1 from 1tls not detected"); // securing requires layering step = rowdy.nextStep(route1s, route1); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1s from 1 not detected"); // securing requires layering, and multiple layers are not supported step = rowdy.nextStep(route1tls, route1tl); Assertions.assertEquals(HttpRouteDirector.UNREACHABLE, step, "unreachable route1tls from 1tl not detected"); // cases where tunnelling to the target is required step = rowdy.nextStep(route1t, route1); Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1t from 1"); step = rowdy.nextStep(route1tl, route1); Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1tl from 1"); step = rowdy.nextStep(route1tls, route1); Assertions.assertEquals(HttpRouteDirector.TUNNEL_TARGET, step, "wrong step to route1tls from 1"); // cases where layering on the tunnel is required step = rowdy.nextStep(route1tl, route1t); Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tl from 1t"); step = rowdy.nextStep(route1tl, route1ts); Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tl from 1ts"); step = rowdy.nextStep(route1tls, route1t); Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tls from 1t"); step = rowdy.nextStep(route1tls, route1ts); Assertions.assertEquals(HttpRouteDirector.LAYER_PROTOCOL, step, "wrong step to route1tls from 1ts"); // There are some odd cases left over, like having a secure tunnel // that becomes unsecure by layering, or a secure connection to a // proxy that becomes unsecure by tunnelling to another proxy. } } TestRouteTracker.java000066400000000000000000000611171434266521000374030ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import java.net.InetAddress; import java.util.HashSet; import java.util.Set; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo.LayerType; import org.apache.hc.client5.http.RouteInfo.TunnelType; import org.apache.hc.client5.http.RouteTracker; import org.apache.hc.client5.http.routing.HttpRouteDirector; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link RouteTracker}. */ @SuppressWarnings("boxing") // test code public class TestRouteTracker { // a selection of constants for generating routes public final static HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80); public final static HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080); // It is not necessary to have extra targets for https. // The 'layered' and 'secure' flags are specified explicitly // for routes, they will not be determined from the scheme. public final static HttpHost PROXY1 = new HttpHost("proxy1.test.invalid", 80); public final static HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080); public final static HttpHost PROXY3 = new HttpHost("proxy3.test.invalid", 88); public final static InetAddress LOCAL41; public final static InetAddress LOCAL42; public final static InetAddress LOCAL61; public final static InetAddress LOCAL62; // need static initializer to deal with exceptions static { try { LOCAL41 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 }); LOCAL42 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 2 }); LOCAL61 = InetAddress.getByAddress(new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }); LOCAL62 = InetAddress.getByAddress(new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }); } catch (final Exception x) { throw new ExceptionInInitializerError(x); } } @SuppressWarnings("unused") @Test public void testCstrTargetLocal() { RouteTracker rt = new RouteTracker(TARGET1, null); Assertions.assertEquals(TARGET1, rt.getTargetHost(), "wrong target (target,null)"); Assertions.assertNull(rt.getLocalAddress(), "wrong local address (target,null)"); Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (target,null)"); Assertions.assertNull(rt.getProxyHost(), "wrong proxy (target,null)"); Assertions.assertNull(rt.toRoute(), "wrong route (target,null)"); checkCTLS(rt, false, false, false, false); rt = new RouteTracker(TARGET2, LOCAL61); Assertions.assertEquals(TARGET2, rt.getTargetHost(), "wrong target (target,local)"); Assertions.assertEquals(LOCAL61, rt.getLocalAddress(), "wrong local address (target,local)"); Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (target,local)"); Assertions.assertNull(rt.getProxyHost(), "wrong proxy (target,local)"); Assertions.assertNull(rt.toRoute(), "wrong route (target,local)"); checkCTLS(rt, false, false, false, false); Assertions.assertThrows(NullPointerException.class, () -> new RouteTracker(null, LOCAL41)); } @SuppressWarnings("unused") @Test public void testCstrRoute() { HttpRoute r = new HttpRoute(TARGET1); RouteTracker rt = new RouteTracker(r); Assertions.assertEquals(TARGET1, rt.getTargetHost(), "wrong target (r1)"); Assertions.assertNull(rt.getLocalAddress(), "wrong local address (r1)"); Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (r1)"); Assertions.assertNull(rt.getProxyHost(), "wrong proxy (r1)"); Assertions.assertNull(rt.toRoute(), "wrong route (r1)"); checkCTLS(rt, false, false, false, false); r = new HttpRoute(TARGET2, LOCAL61, true); rt = new RouteTracker(r); Assertions.assertEquals(TARGET2, rt.getTargetHost(), "wrong target (r2)"); Assertions.assertEquals(LOCAL61, rt.getLocalAddress(), "wrong local address (r2)"); Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (r2)"); Assertions.assertNull(rt.getProxyHost(), "wrong proxy (r2)"); Assertions.assertNull(rt.toRoute(), "wrong route (r2)"); checkCTLS(rt, false, false, false, false); r = new HttpRoute(TARGET1, LOCAL42, PROXY3, true); rt = new RouteTracker(r); Assertions.assertEquals(TARGET1, rt.getTargetHost(), "wrong target (r3)"); Assertions.assertEquals(LOCAL42, rt.getLocalAddress(), "wrong local address (r3)"); Assertions.assertEquals(0, rt.getHopCount(), "wrong hop count (r3)"); Assertions.assertNull(rt.getProxyHost(), "wrong proxy (r3)"); Assertions.assertNull(rt.toRoute(), "wrong route (r3)"); checkCTLS(rt, false, false, false, false); Assertions.assertThrows(NullPointerException.class, () -> new RouteTracker(null)); } @Test public void testIllegalArgs() { final RouteTracker rt = new RouteTracker(TARGET2, null); Assertions.assertThrows(NullPointerException.class, () -> rt.connectProxy(null, true)); Assertions.assertThrows(NullPointerException.class, () -> rt.connectProxy(null, false)); rt.connectProxy(PROXY1, false); Assertions.assertThrows(NullPointerException.class, () -> rt.tunnelProxy(null, false)); Assertions.assertThrows(NullPointerException.class, () -> rt.tunnelProxy(null, true)); Assertions.assertThrows(IllegalArgumentException.class, () -> rt.getHopTarget(-1)); Assertions.assertThrows(IllegalArgumentException.class, () -> rt.getHopTarget(2)); } @Test public void testIllegalStates() { final RouteTracker rt = new RouteTracker(TARGET1, null); Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelTarget(false)); Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelProxy(PROXY1, false)); Assertions.assertThrows(IllegalStateException.class, () -> rt.layerProtocol(true)); // connect directly rt.connectTarget(false); Assertions.assertThrows(IllegalStateException.class, () -> rt.connectTarget(false)); Assertions.assertThrows(IllegalStateException.class, () -> rt.connectProxy(PROXY2, false)); Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelTarget(false)); Assertions.assertThrows(IllegalStateException.class, () -> rt.tunnelProxy(PROXY1, false)); } @Test public void testDirectRoutes() { final HttpRouteDirector rd = BasicRouteDirector.INSTANCE; HttpRoute r = new HttpRoute(TARGET1, LOCAL41, false); RouteTracker rt = new RouteTracker(r); boolean complete = checkVia(rt, r, rd, 2); Assertions.assertTrue(complete, "incomplete route 1"); r = new HttpRoute(TARGET2, LOCAL62, true); rt = new RouteTracker(r); complete = checkVia(rt, r, rd, 2); Assertions.assertTrue(complete, "incomplete route 2"); } @Test public void testProxyRoutes() { final HttpRouteDirector rd = BasicRouteDirector.INSTANCE; HttpRoute r = new HttpRoute(TARGET2, null, PROXY1, false); RouteTracker rt = new RouteTracker(r); boolean complete = checkVia(rt, r, rd, 2); Assertions.assertTrue(complete, "incomplete route 1"); // tunnelled, but neither secure nor layered r = new HttpRoute(TARGET1, LOCAL61, PROXY3, false, TunnelType.TUNNELLED, LayerType.PLAIN); rt = new RouteTracker(r); complete = checkVia(rt, r, rd, 3); Assertions.assertTrue(complete, "incomplete route 2"); // tunnelled, layered, but not secure r = new HttpRoute(TARGET1, LOCAL61, PROXY3, false, TunnelType.TUNNELLED, LayerType.LAYERED); rt = new RouteTracker(r); complete = checkVia(rt, r, rd, 4); Assertions.assertTrue(complete, "incomplete route 3"); // tunnelled, layered, secure r = new HttpRoute(TARGET1, LOCAL61, PROXY3, true); rt = new RouteTracker(r); complete = checkVia(rt, r, rd, 4); Assertions.assertTrue(complete, "incomplete route 4"); } @Test public void testProxyChainRoutes() { final HttpRouteDirector rd = BasicRouteDirector.INSTANCE; HttpHost[] proxies = { PROXY1, PROXY2 }; HttpRoute r = new HttpRoute(TARGET2, LOCAL42, proxies, false, TunnelType.PLAIN, LayerType.PLAIN); RouteTracker rt = new RouteTracker(r); boolean complete = checkVia(rt, r, rd, 3); Assertions.assertTrue(complete, "incomplete route 1"); // tunnelled, but neither secure nor layered proxies = new HttpHost[]{ PROXY3, PROXY2 }; r = new HttpRoute(TARGET1, null, proxies, false, TunnelType.TUNNELLED, LayerType.PLAIN); rt = new RouteTracker(r); complete = checkVia(rt, r, rd, 4); Assertions.assertTrue(complete, "incomplete route 2"); // tunnelled, layered, but not secure proxies = new HttpHost[]{ PROXY3, PROXY2, PROXY1 }; r = new HttpRoute(TARGET2, LOCAL61, proxies, false, TunnelType.TUNNELLED, LayerType.LAYERED); rt = new RouteTracker(r); complete = checkVia(rt, r, rd, 6); Assertions.assertTrue(complete, "incomplete route 3"); // tunnelled, layered, secure proxies = new HttpHost[]{ PROXY1, PROXY3 }; r = new HttpRoute(TARGET1, LOCAL61, proxies, true, TunnelType.TUNNELLED, LayerType.LAYERED); rt = new RouteTracker(r); complete = checkVia(rt, r, rd, 5); Assertions.assertTrue(complete, "incomplete route 4"); } @Test public void testEqualsHashcodeCloneToString() throws CloneNotSupportedException { final RouteTracker rt0 = new RouteTracker(TARGET1, null); final RouteTracker rt1 = new RouteTracker(TARGET2, null); final RouteTracker rt2 = new RouteTracker(TARGET1, null); final RouteTracker rt3 = new RouteTracker(TARGET1, null); final RouteTracker rt4 = new RouteTracker(TARGET1, LOCAL41); final RouteTracker rt6 = new RouteTracker(TARGET1, LOCAL62); Assertions.assertNotEquals(null, rt0, "rt0"); Assertions.assertEquals(rt0, rt0, "rt0"); Assertions.assertNotEquals("rt0", rt0, rt0.toString()); Assertions.assertNotEquals(rt0, rt4, "rt0 == rt4"); Assertions.assertNotEquals(rt0, rt1, "rt0 == rt1"); // Check host takes part in equals // Check that connection takes part in equals Assertions.assertEquals(rt0, rt2, "rt0 != rt2"); rt2.connectTarget(false); Assertions.assertNotEquals(rt0, rt2, "rt0 == rt2"); Assertions.assertEquals(rt0, rt3, "rt0 != rt3"); rt3.connectTarget(true); Assertions.assertNotEquals(rt0, rt3, "rt0 == rt3"); Assertions.assertNotEquals(rt2, rt3, "rt2 == rt3"); // Test secure takes part // TODO needs tests for tunnel and layered Assertions.assertNotEquals(rt4, rt0, "rt4 == rt0"); Assertions.assertNotEquals(rt0, rt6, "rt0 == rt6"); Assertions.assertNotEquals(rt6, rt0, "rt6 == rt0"); Assertions.assertNotEquals(rt4, rt6, "rt4 == rt6"); Assertions.assertNotEquals(rt6, rt4, "rt6 == rt4"); // it is likely but not guaranteed that the hashcodes are different Assertions.assertNotEquals(rt0.hashCode(), rt4.hashCode(), "rt0 == rt4 (hashcode)"); Assertions.assertNotEquals(rt0.hashCode(), rt6.hashCode(), "rt0 == rt6 (hashcode)"); Assertions.assertNotEquals(rt6.hashCode(), rt4.hashCode(), "rt6 == rt4 (hashcode)"); Assertions.assertEquals(rt0, rt0.clone(), "rt0 (clone)"); Assertions.assertEquals(rt4, rt4.clone(), "rt4 (clone)"); Assertions.assertEquals(rt6, rt6.clone(), "rt6 (clone)"); // we collect (clones of) the different tracked routes along the way // rt0 -> direct connection // rt1 -> via single proxy // rt2 -> via proxy chain final Set hs = new HashSet<>(); // we also collect hashcodes for the different paths // since we can't guarantee what influence the HttpHost hashcodes have, // we keep separate sets here final Set hc0 = new HashSet<>(); final Set hc4 = new HashSet<>(); final Set hc6 = new HashSet<>(); RouteTracker rt = null; Assertions.assertTrue(hs.add(rt0)); Assertions.assertTrue(hs.add(rt4)); Assertions.assertTrue(hs.add(rt6)); Assertions.assertTrue(hc0.add(rt0.hashCode())); Assertions.assertTrue(hc4.add(rt4.hashCode())); Assertions.assertTrue(hc6.add(rt6.hashCode())); rt = (RouteTracker) rt0.clone(); rt.connectTarget(false); Assertions.assertTrue(hs.add(rt)); Assertions.assertTrue(hc0.add(rt.hashCode())); rt = (RouteTracker) rt0.clone(); rt.connectTarget(true); Assertions.assertTrue(hs.add(rt)); Assertions.assertTrue(hc0.add(rt.hashCode())); // proxy (insecure) -> tunnel (insecure) -> layer (secure) rt = (RouteTracker) rt4.clone(); rt.connectProxy(PROXY1, false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // this is not guaranteed to be unique... Assertions.assertTrue(hc4.add(rt.hashCode())); rt.tunnelTarget(false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc4.add(rt.hashCode())); rt.layerProtocol(true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc4.add(rt.hashCode())); // proxy (secure) -> tunnel (secure) -> layer (insecure) rt = (RouteTracker) rt4.clone(); rt.connectProxy(PROXY1, true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // this is not guaranteed to be unique... Assertions.assertTrue(hc4.add(rt.hashCode())); rt.tunnelTarget(true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc4.add(rt.hashCode())); rt.layerProtocol(false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc4.add(rt.hashCode())); // PROXY1/i -> PROXY2/i -> tunnel/i -> layer/s rt = (RouteTracker) rt6.clone(); rt.connectProxy(PROXY1, false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // this is not guaranteed to be unique... Assertions.assertTrue(hc6.add(rt.hashCode())); rt.tunnelProxy(PROXY2, false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // this is not guaranteed to be unique... Assertions.assertTrue(hc6.add(rt.hashCode())); rt.tunnelTarget(false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc6.add(rt.hashCode())); rt.layerProtocol(true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc6.add(rt.hashCode())); // PROXY1/s -> PROXY2/s -> tunnel/s -> layer/i rt = (RouteTracker) rt6.clone(); rt.connectProxy(PROXY1, true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // this is not guaranteed to be unique... Assertions.assertTrue(hc6.add(rt.hashCode())); rt.tunnelProxy(PROXY2, true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // this is not guaranteed to be unique... Assertions.assertTrue(hc6.add(rt.hashCode())); rt.tunnelTarget(true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc6.add(rt.hashCode())); rt.layerProtocol(false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); Assertions.assertTrue(hc6.add(rt.hashCode())); // PROXY2/i -> PROXY1/i -> tunnel/i -> layer/s rt = (RouteTracker) rt6.clone(); rt.connectProxy(PROXY2, false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // this is not guaranteed to be unique... Assertions.assertTrue(hc6.add(rt.hashCode())); rt.tunnelProxy(PROXY1, false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // proxy chain sequence does not affect hashcode, so duplicate: // Assertions.assertTrue(hc6.add(Integer.valueOf(rt.hashCode()))); rt.tunnelTarget(false); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // proxy chain sequence does not affect hashcode, so duplicate: // Assertions.assertTrue(hc6.add(Integer.valueOf(rt.hashCode()))); rt.layerProtocol(true); Assertions.assertTrue(hs.add((RouteTracker) rt.clone())); // proxy chain sequence does not affect hashcode, so duplicate: // Assertions.assertTrue(hc6.add(Integer.valueOf(rt.hashCode()))); // check that all toString are OK and different final Set rtstrings = new HashSet<>(); for (final RouteTracker current: hs) { final String rts = checkToString(current); Assertions.assertTrue(rtstrings.add(rts), "duplicate toString: " + rts); } } /** Helper to check the status of the four flags. */ public final static void checkCTLS(final RouteTracker rt, final boolean c, final boolean t, final boolean l, final boolean s) { final String rts = rt.toString(); Assertions.assertEquals(c, rt.isConnected(), "wrong flag connected: " + rts); Assertions.assertEquals(t, rt.isTunnelled(), "wrong flag tunnelled: " + rts); Assertions.assertEquals(t ? TunnelType.TUNNELLED : TunnelType.PLAIN, rt.getTunnelType(), "wrong enum tunnelled: " + rts); Assertions.assertEquals(l, rt.isLayered(), "wrong flag layered: " + rts); Assertions.assertEquals(l ? LayerType.LAYERED : LayerType.PLAIN, rt.getLayerType(), "wrong enum layered: " + rts); Assertions.assertEquals(s, rt.isSecure(), "wrong flag secure: " + rts); } /** * Helper to check tracking of a route. * This uses a {@link HttpRouteDirector} to fake establishing the route, * checking the intermediate steps. * * @param rt the tracker to check with * @param r the route to establish * @param rd the director to check with * @param steps the step count for this invocation * * @return {@code true} iff the route is complete */ public final static boolean checkVia(final RouteTracker rt, final HttpRoute r, final HttpRouteDirector rd, final int steps) { final String msg = r + " @ " + rt; boolean complete = false; int n = steps; while (!complete && (n > 0)) { final int action = rd.nextStep(r, rt.toRoute()); switch (action) { case HttpRouteDirector.COMPLETE: complete = true; Assertions.assertEquals(r, rt.toRoute()); break; case HttpRouteDirector.CONNECT_TARGET: { final boolean sec = r.isSecure(); rt.connectTarget(sec); checkCTLS(rt, true, false, false, sec); Assertions.assertEquals(1, rt.getHopCount(), "wrong hop count "+msg); Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(0), "wrong hop0 "+msg); } break; case HttpRouteDirector.CONNECT_PROXY: { // we assume an insecure proxy connection final boolean sec = false; rt.connectProxy(r.getProxyHost(), sec); checkCTLS(rt, true, false, false, sec); Assertions.assertEquals(2, rt.getHopCount(), "wrong hop count "+msg); Assertions.assertEquals(r.getProxyHost(), rt.getHopTarget(0), "wrong hop0 "+msg); Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(1), "wrong hop1 "+msg); } break; case HttpRouteDirector.TUNNEL_TARGET: { final int hops = rt.getHopCount(); // we assume an insecure tunnel final boolean sec = false; rt.tunnelTarget(sec); checkCTLS(rt, true, true, false, sec); Assertions.assertEquals(hops, rt.getHopCount(), "wrong hop count "+msg); Assertions.assertEquals(r.getProxyHost(), rt.getHopTarget(0), "wrong hop0 "+msg); Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(hops-1), "wrong hopN "+msg); } break; case HttpRouteDirector.TUNNEL_PROXY: { final int hops = rt.getHopCount(); // before tunnelling // we assume an insecure tunnel final boolean sec = false; final HttpHost pxy = r.getHopTarget(hops-1); rt.tunnelProxy(pxy, sec); // Since we're tunnelling to a proxy and not the target, // the 'tunelling' flag is false: no end-to-end tunnel. checkCTLS(rt, true, false, false, sec); Assertions.assertEquals(hops+1, rt.getHopCount(), "wrong hop count "+msg); Assertions.assertEquals(r.getProxyHost(), rt.getHopTarget(0), "wrong hop0 "+msg); Assertions.assertEquals(pxy, rt.getHopTarget(hops-1), "wrong hop"+hops+" "+msg); Assertions.assertEquals(r.getTargetHost(), rt.getHopTarget(hops), "wrong hopN "+msg); } break; case HttpRouteDirector.LAYER_PROTOCOL: { final int hops = rt.getHopCount(); final boolean tun = rt.isTunnelled(); final boolean sec = r.isSecure(); rt.layerProtocol(sec); checkCTLS(rt, true, tun, true, sec); Assertions.assertEquals(hops, rt.getHopCount(), "wrong hop count "+msg); Assertions.assertEquals(r.getProxyHost(), rt.getProxyHost(), "wrong proxy "+msg); Assertions.assertEquals(r.getTargetHost(), rt.getTargetHost(), "wrong target "+msg); } break; // UNREACHABLE default: Assertions.fail("unexpected action " + action + " from director, "+msg); break; } // switch n--; } return complete; } // checkVia /** * Checks the output of {@code toString}. * * @param rt the tracker for which to check the output * * @return the result of {@code rt.toString()} */ public final static String checkToString(final RouteTracker rt) { if (rt == null) { return null; } final String rts = rt.toString(); if (rt.getLocalAddress() != null) { final String las = rt.getLocalAddress().toString(); Assertions.assertTrue(rts.contains(las), "no local address in toString(): " + rts); } for (int i=0; i. * */ package org.apache.hc.client5.http.impl.routing; import static org.hamcrest.MatcherAssert.assertThat; import java.net.InetAddress; import java.net.URI; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.net.URIAuthority; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRoutingSupport { @Test public void testDetermineHost() throws Exception { final HttpRequest request1 = new BasicHttpRequest("GET", "/"); final HttpHost host1 = RoutingSupport.determineHost(request1); assertThat(host1, CoreMatchers.nullValue()); final HttpRequest request2 = new BasicHttpRequest("GET", new URI("https://somehost:8443/")); final HttpHost host2 = RoutingSupport.determineHost(request2); assertThat(host2, CoreMatchers.equalTo(new HttpHost("https", "somehost", 8443))); } @Test public void testDetermineHostMissingScheme() throws Exception { final HttpRequest request1 = new BasicHttpRequest("GET", "/"); request1.setAuthority(new URIAuthority("host")); Assertions.assertThrows(ProtocolException.class, () -> RoutingSupport.determineHost(request1)); } @Test public void testNormalizeHost() throws Exception { Assertions.assertEquals(new HttpHost("http", "somehost", 80), RoutingSupport.normalize( new HttpHost("http", "somehost", -1), DefaultSchemePortResolver.INSTANCE)); Assertions.assertEquals(new HttpHost("https", "somehost", 443), RoutingSupport.normalize( new HttpHost("https", "somehost", -1), DefaultSchemePortResolver.INSTANCE)); Assertions.assertEquals(new HttpHost("http", InetAddress.getLocalHost(), "localhost", 80), RoutingSupport.normalize( new HttpHost("http", InetAddress.getLocalHost(), "localhost", -1), DefaultSchemePortResolver.INSTANCE)); Assertions.assertEquals(new HttpHost("http", "somehost", 8080), RoutingSupport.normalize( new HttpHost("http", "somehost", 8080), DefaultSchemePortResolver.INSTANCE)); } } TestSystemDefaultRoutePlanner.java000066400000000000000000000106721434266521000421210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/impl/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.impl.routing; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.ProxySelector; import java.net.URI; import java.util.ArrayList; import java.util.List; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; /** * Tests for {@link SystemDefaultRoutePlanner}. */ @SuppressWarnings("boxing") // test code public class TestSystemDefaultRoutePlanner { private SchemePortResolver schemePortResolver; private ProxySelector proxySelector; private SystemDefaultRoutePlanner routePlanner; @BeforeEach public void setup() { schemePortResolver = Mockito.mock(SchemePortResolver.class); proxySelector = Mockito.mock(ProxySelector.class); routePlanner = new SystemDefaultRoutePlanner(schemePortResolver, proxySelector); } @Test public void testDirect() throws Exception { final HttpHost target = new HttpHost("http", "somehost", 80); final HttpContext context = new BasicHttpContext(); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(target, route.getTargetHost()); Assertions.assertEquals(1, route.getHopCount()); Assertions.assertFalse(route.isSecure()); Mockito.verify(schemePortResolver, Mockito.never()).resolve(ArgumentMatchers.any()); } @Test public void testDirectDefaultPort() throws Exception { final HttpHost target = new HttpHost("https", "somehost", -1); Mockito.when(schemePortResolver.resolve(target)).thenReturn(443); final HttpContext context = new BasicHttpContext(); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(new HttpHost("https", "somehost", 443), route.getTargetHost()); Assertions.assertEquals(1, route.getHopCount()); Assertions.assertTrue(route.isSecure()); } @Test public void testProxy() throws Exception { final InetAddress ia = InetAddress.getByAddress(new byte[] { (byte)127, (byte)0, (byte)0, (byte)1 }); final InetSocketAddress isa1 = new InetSocketAddress(ia, 11111); final InetSocketAddress isa2 = new InetSocketAddress(ia, 22222); final List proxies = new ArrayList<>(2); proxies.add(new Proxy(Proxy.Type.HTTP, isa1)); proxies.add(new Proxy(Proxy.Type.HTTP, isa2)); Mockito.when(proxySelector.select(new URI("http://somehost:80"))).thenReturn(proxies); final HttpHost target = new HttpHost("http", "somehost", 80); final HttpContext context = new BasicHttpContext(); final HttpRoute route = routePlanner.determineRoute(target, context); Assertions.assertEquals(target, route.getTargetHost()); Assertions.assertEquals(2, route.getHopCount()); Assertions.assertEquals(isa1.getPort(), route.getProxyHost().getPort()); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/000077500000000000000000000000001434266521000325505ustar00rootroot00000000000000TestRedirectLocation.java000066400000000000000000000047111434266521000374310ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.net.URI; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Simple tests for {@link RedirectLocations}. */ public class TestRedirectLocation { @Test public void testBasics() throws Exception { final RedirectLocations locations = new RedirectLocations(); final URI uri1 = new URI("/this"); final URI uri2 = new URI("/that"); final URI uri3 = new URI("/this-and-that"); locations.add(uri1); locations.add(uri2); locations.add(uri2); locations.add(uri3); locations.add(uri3); Assertions.assertTrue(locations.contains(uri1)); Assertions.assertTrue(locations.contains(uri2)); Assertions.assertTrue(locations.contains(uri3)); Assertions.assertFalse(locations.contains(new URI("/"))); final List list = locations.getAll(); Assertions.assertNotNull(list); Assertions.assertEquals(5, list.size()); Assertions.assertEquals(uri1, list.get(0)); Assertions.assertEquals(uri2, list.get(1)); Assertions.assertEquals(uri2, list.get(2)); Assertions.assertEquals(uri3, list.get(3)); Assertions.assertEquals(uri3, list.get(4)); } } TestRequestAddCookies.java000066400000000000000000000451141434266521000375570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.time.Instant; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo.LayerType; import org.apache.hc.client5.http.RouteInfo.TunnelType; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.cookie.StandardCookieSpec; import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; import org.apache.hc.client5.http.impl.cookie.IgnoreCookieSpecFactory; import org.apache.hc.client5.http.impl.cookie.RFC6265CookieSpecFactory; import org.apache.hc.client5.http.impl.cookie.RFC6265StrictSpec; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.config.Lookup; import org.apache.hc.core5.http.config.RegistryBuilder; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; public class TestRequestAddCookies { private HttpHost target; private CookieStore cookieStore; private Lookup cookieSpecRegistry; @BeforeEach public void setUp() { this.target = new HttpHost("localhost.local", 80); this.cookieStore = new BasicCookieStore(); final BasicClientCookie cookie1 = new BasicClientCookie("name1", "value1"); cookie1.setDomain("localhost.local"); cookie1.setPath("/"); this.cookieStore.addCookie(cookie1); final BasicClientCookie cookie2 = new BasicClientCookie("name2", "value2"); cookie2.setDomain("localhost.local"); cookie2.setPath("/"); this.cookieStore.addCookie(cookie2); this.cookieSpecRegistry = RegistryBuilder.create() .register(StandardCookieSpec.RELAXED, new RFC6265CookieSpecFactory( RFC6265CookieSpecFactory.CompatibilityLevel.RELAXED, null)) .register(StandardCookieSpec.STRICT, new RFC6265CookieSpecFactory( RFC6265CookieSpecFactory.CompatibilityLevel.STRICT, null)) .register(StandardCookieSpec.IGNORE, new IgnoreCookieSpecFactory()) .build(); } @Test public void testRequestParameterCheck() throws Exception { final HttpClientContext context = HttpClientContext.create(); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, context)); } @Test public void testContextParameterCheck() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(request, null, null)); } @Test public void testAddCookies() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertNotNull(headers); Assertions.assertEquals(1, headers.length); Assertions.assertEquals("name1=value1; name2=value2", headers[0].getValue()); final CookieOrigin cookieOrigin = context.getCookieOrigin(); Assertions.assertNotNull(cookieOrigin); Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost()); Assertions.assertEquals(this.target.getPort(), cookieOrigin.getPort()); Assertions.assertEquals("/", cookieOrigin.getPath()); Assertions.assertFalse(cookieOrigin.isSecure()); } @Test public void testCookiesForConnectRequest() throws Exception { final HttpRequest request = new BasicHttpRequest("CONNECT", "www.somedomain.com"); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertNotNull(headers); Assertions.assertEquals(0, headers.length); } @Test public void testNoCookieStore() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, null); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertNotNull(headers); Assertions.assertEquals(0, headers.length); } @Test public void testNoCookieSpecRegistry() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, null); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertNotNull(headers); Assertions.assertEquals(0, headers.length); } @Test public void testNoHttpConnection() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, null); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertNotNull(headers); Assertions.assertEquals(0, headers.length); } @Test public void testAddCookiesUsingExplicitCookieSpec() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final RequestConfig config = RequestConfig.custom() .setCookieSpec(StandardCookieSpec.STRICT) .build(); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final CookieSpec cookieSpec = context.getCookieSpec(); Assertions.assertTrue(cookieSpec instanceof RFC6265StrictSpec); final Header[] headers1 = request.getHeaders("Cookie"); Assertions.assertNotNull(headers1); Assertions.assertEquals(1, headers1.length); Assertions.assertEquals("name1=value1; name2=value2", headers1[0].getValue()); } @Test public void testAuthScopeInvalidRequestURI() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "crap:"); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); } @Test public void testAuthScopeRemotePortWhenDirect() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/stuff"); this.target = new HttpHost("localhost.local"); final HttpRoute route = new HttpRoute(new HttpHost("localhost.local", 1234), null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final CookieOrigin cookieOrigin = context.getCookieOrigin(); Assertions.assertNotNull(cookieOrigin); Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost()); Assertions.assertEquals(1234, cookieOrigin.getPort()); Assertions.assertEquals("/stuff", cookieOrigin.getPath()); Assertions.assertFalse(cookieOrigin.isSecure()); } @Test public void testAuthDefaultHttpPortWhenProxy() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/stuff"); this.target = new HttpHost("localhost.local"); final HttpRoute route = new HttpRoute( new HttpHost("localhost.local", 80), null, new HttpHost("localhost", 8888), false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final CookieOrigin cookieOrigin = context.getCookieOrigin(); Assertions.assertNotNull(cookieOrigin); Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost()); Assertions.assertEquals(80, cookieOrigin.getPort()); Assertions.assertEquals("/stuff", cookieOrigin.getPath()); Assertions.assertFalse(cookieOrigin.isSecure()); } @Test public void testAuthDefaultHttpsPortWhenProxy() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/stuff"); this.target = new HttpHost("https", "localhost", -1); final HttpRoute route = new HttpRoute( new HttpHost("https", "localhost", 443), null, new HttpHost("http", "localhost", 8888), true, TunnelType.TUNNELLED, LayerType.LAYERED); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final CookieOrigin cookieOrigin = context.getCookieOrigin(); Assertions.assertNotNull(cookieOrigin); Assertions.assertEquals(this.target.getHostName(), cookieOrigin.getHost()); Assertions.assertEquals(443, cookieOrigin.getPort()); Assertions.assertEquals("/stuff", cookieOrigin.getPath()); Assertions.assertTrue(cookieOrigin.isSecure()); } @Test public void testExcludeExpiredCookies() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final BasicClientCookie cookie3 = new BasicClientCookie("name3", "value3"); cookie3.setDomain("localhost.local"); cookie3.setPath("/"); cookie3.setExpiryDate(Instant.now().plusMillis(100)); this.cookieStore.addCookie(cookie3); Assertions.assertEquals(3, this.cookieStore.getCookies().size()); this.cookieStore = Mockito.spy(this.cookieStore); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); // Make sure the third cookie expires Thread.sleep(200); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertNotNull(headers); Assertions.assertEquals(1, headers.length); Assertions.assertEquals("name1=value1; name2=value2", headers[0].getValue()); Mockito.verify(this.cookieStore, Mockito.times(1)).clearExpired(ArgumentMatchers.any(Instant.class)); } @Test public void testNoMatchingCookies() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); this.cookieStore.clear(); final BasicClientCookie cookie3 = new BasicClientCookie("name3", "value3"); cookie3.setDomain("www.somedomain.com"); cookie3.setPath("/"); this.cookieStore.addCookie(cookie3); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers = request.getHeaders("Cookie"); Assertions.assertNotNull(headers); Assertions.assertEquals(0, headers.length); } // Helper method private BasicClientCookie makeCookie(final String name, final String value, final String domain, final String path) { final BasicClientCookie cookie = new BasicClientCookie(name, value); cookie.setDomain(domain); cookie.setPath(path); return cookie; } @Test // Test for ordering adapted from test in Commons HC 3.1 public void testCookieOrder() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/foobar/yada/yada"); this.cookieStore.clear(); cookieStore.addCookie(makeCookie("nomatch", "value", "localhost.local", "/noway")); cookieStore.addCookie(makeCookie("name2", "value", "localhost.local", "/foobar/yada")); cookieStore.addCookie(makeCookie("name3", "value", "localhost.local", "/foobar")); cookieStore.addCookie(makeCookie("name1", "value", "localhost.local", "/foobar/yada/yada")); final HttpRoute route = new HttpRoute(this.target, null, false); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry); final HttpRequestInterceptor interceptor = RequestAddCookies.INSTANCE; interceptor.process(request, null, context); final Header[] headers1 = request.getHeaders("Cookie"); Assertions.assertNotNull(headers1); Assertions.assertEquals(1, headers1.length); Assertions.assertEquals("name1=value; name2=value; name3=value", headers1[0].getValue()); } } TestRequestClientConnControl.java000066400000000000000000000140721434266521000411460ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo.LayerType; import org.apache.hc.client5.http.RouteInfo.TunnelType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRequestClientConnControl { @Test public void testRequestParameterCheck() throws Exception { final HttpClientContext context = HttpClientContext.create(); final HttpRequestInterceptor interceptor = new RequestClientConnControl(); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, context)); } @Test public void testConnectionKeepAliveForConnectRequest() throws Exception { final HttpRequest request = new BasicHttpRequest("CONNECT", "www.somedomain.com"); final HttpClientContext context = HttpClientContext.create(); final HttpRequestInterceptor interceptor = new RequestClientConnControl(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNull(header); } @Test public void testConnectionKeepAliveForDirectRequests() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpClientContext context = HttpClientContext.create(); final HttpHost target = new HttpHost("http", "localhost", 80); final HttpRoute route = new HttpRoute(target, null, false); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); final HttpRequestInterceptor interceptor = new RequestClientConnControl(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals(HeaderElements.KEEP_ALIVE, header.getValue()); } @Test public void testConnectionKeepAliveForTunneledRequests() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpClientContext context = HttpClientContext.create(); final HttpHost target = new HttpHost("https", "localhost", 443); final HttpHost proxy = new HttpHost("localhost", 8080); final HttpRoute route = new HttpRoute(target, null, proxy, true, TunnelType.TUNNELLED, LayerType.LAYERED); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); final HttpRequestInterceptor interceptor = new RequestClientConnControl(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals(HeaderElements.KEEP_ALIVE, header.getValue()); } @Test public void testProxyConnectionKeepAliveForRequestsOverProxy() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpClientContext context = HttpClientContext.create(); final HttpHost target = new HttpHost("http", "localhost", 80); final HttpHost proxy = new HttpHost("localhost", 8080); final HttpRoute route = new HttpRoute(target, null, proxy, false, TunnelType.PLAIN, LayerType.PLAIN); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); final HttpRequestInterceptor interceptor = new RequestClientConnControl(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNull(header); } @Test public void testPreserveCustomConnectionHeader() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); request.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE); final HttpClientContext context = HttpClientContext.create(); final HttpHost target = new HttpHost("https", "localhost", 443); final HttpHost proxy = new HttpHost("localhost", 8080); final HttpRoute route = new HttpRoute(target, null, proxy, true, TunnelType.TUNNELLED, LayerType.LAYERED); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); final HttpRequestInterceptor interceptor = new RequestClientConnControl(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HttpHeaders.CONNECTION); Assertions.assertNotNull(header); Assertions.assertEquals(HeaderElements.CLOSE, header.getValue()); } } TestRequestDefaultHeaders.java000066400000000000000000000066141434266521000404340ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.util.ArrayList; import java.util.List; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequestInterceptor; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRequestDefaultHeaders { @Test public void testRequestParameterCheck() throws Exception { final HttpContext context = new BasicHttpContext(); final HttpRequestInterceptor interceptor = RequestDefaultHeaders.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, context)); } @Test public void testNoDefaultHeadersForConnectRequest() throws Exception { final HttpRequest request = new BasicHttpRequest("CONNECT", "www.somedomain.com"); final List
defheaders = new ArrayList<>(); defheaders.add(new BasicHeader("custom", "stuff")); final HttpContext context = new BasicHttpContext(); final HttpRequestInterceptor interceptor = new RequestDefaultHeaders(defheaders); interceptor.process(request, null, context); final Header header1 = request.getFirstHeader("custom"); Assertions.assertNull(header1); } @Test public void testDefaultHeaders() throws Exception { final HttpRequest request = new BasicHttpRequest("GET", "/"); request.addHeader("custom", "stuff"); final List
defheaders = new ArrayList<>(); defheaders.add(new BasicHeader("custom", "other stuff")); final HttpContext context = new BasicHttpContext(); final HttpRequestInterceptor interceptor = new RequestDefaultHeaders(defheaders); interceptor.process(request, null, context); final Header[] headers = request.getHeaders("custom"); Assertions.assertNotNull(headers); Assertions.assertEquals(1, headers.length); Assertions.assertEquals("stuff", headers[0].getValue()); } } TestRequestExpectContinue.java000066400000000000000000000136411434266521000405070ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.nio.charset.StandardCharsets; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HeaderElements; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.BasicHttpContext; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestRequestExpectContinue { @Test public void testRequestExpectContinueGenerated() throws Exception { final HttpClientContext context = HttpClientContext.create(); final RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(true).build(); context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/"); final String s = "whatever"; final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII); request.setEntity(entity); final RequestExpectContinue interceptor = new RequestExpectContinue(); interceptor.process(request, request.getEntity(), context); final Header header = request.getFirstHeader(HttpHeaders.EXPECT); Assertions.assertNotNull(header); Assertions.assertEquals(HeaderElements.CONTINUE, header.getValue()); } @Test public void testRequestExpectContinueNotGenerated() throws Exception { final HttpContext context = new BasicHttpContext(null); final RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(false).build(); context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/"); final String s = "whatever"; final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII); request.setEntity(entity); final RequestExpectContinue interceptor = new RequestExpectContinue(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HeaderElements.CONTINUE); Assertions.assertNull(header); } @Test public void testRequestExpectContinueHTTP10() throws Exception { final HttpContext context = new BasicHttpContext(null); final RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(true).build(); context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/"); request.setVersion(HttpVersion.HTTP_1_0); final String s = "whatever"; final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII); request.setEntity(entity); final RequestExpectContinue interceptor = new RequestExpectContinue(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HeaderElements.CONTINUE); Assertions.assertNull(header); } @Test public void testRequestExpectContinueZeroContent() throws Exception { final HttpContext context = new BasicHttpContext(null); final RequestConfig config = RequestConfig.custom().setExpectContinueEnabled(true).build(); context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/"); final String s = ""; final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII); request.setEntity(entity); final RequestExpectContinue interceptor = new RequestExpectContinue(); interceptor.process(request, null, context); final Header header = request.getFirstHeader(HeaderElements.CONTINUE); Assertions.assertNull(header); } @Test public void testRequestExpectContinueInvalidInput() throws Exception { final RequestExpectContinue interceptor = new RequestExpectContinue(); Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, null)); } @Test public void testRequestExpectContinueIgnoreNonenclosingRequests() throws Exception { final HttpContext context = new BasicHttpContext(null); final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/"); final RequestExpectContinue interceptor = new RequestExpectContinue(); interceptor.process(request, null, context); Assertions.assertEquals(0, request.getHeaders().length); } } TestResponseProcessCookies.java000066400000000000000000000145541434266521000406570ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/protocol/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.protocol; import java.util.List; import org.apache.hc.client5.http.cookie.BasicCookieStore; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieOrigin; import org.apache.hc.client5.http.cookie.CookieSpec; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.impl.cookie.RFC6265LaxSpec; import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponseInterceptor; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestResponseProcessCookies { private CookieOrigin cookieOrigin; private CookieSpec cookieSpec; private CookieStore cookieStore; @BeforeEach public void setUp() throws Exception { this.cookieOrigin = new CookieOrigin("localhost", 80, "/", false); this.cookieSpec = new RFC6265LaxSpec(); this.cookieStore = new BasicCookieStore(); } @Test public void testResponseParameterCheck() throws Exception { final HttpClientContext context = HttpClientContext.create(); final HttpResponseInterceptor interceptor = ResponseProcessCookies.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(null, null, context)); } @Test public void testContextParameterCheck() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); final HttpResponseInterceptor interceptor = ResponseProcessCookies.INSTANCE; Assertions.assertThrows(NullPointerException.class, () -> interceptor.process(response, null, null)); } @Test public void testParseCookies() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Set-Cookie", "name1=value1"); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.COOKIE_ORIGIN, this.cookieOrigin); context.setAttribute(HttpClientContext.COOKIE_SPEC, this.cookieSpec); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); final HttpResponseInterceptor interceptor = ResponseProcessCookies.INSTANCE; interceptor.process(response, null, context); final List cookies = this.cookieStore.getCookies(); Assertions.assertNotNull(cookies); Assertions.assertEquals(1, cookies.size()); final Cookie cookie = cookies.get(0); Assertions.assertEquals("name1", cookie.getName()); Assertions.assertEquals("value1", cookie.getValue()); Assertions.assertEquals("localhost", cookie.getDomain()); Assertions.assertEquals("/", cookie.getPath()); } @Test public void testNoCookieOrigin() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Set-Cookie", "name1=value1"); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.COOKIE_ORIGIN, null); context.setAttribute(HttpClientContext.COOKIE_SPEC, this.cookieSpec); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); final HttpResponseInterceptor interceptor = ResponseProcessCookies.INSTANCE; interceptor.process(response, null, context); final List cookies = this.cookieStore.getCookies(); Assertions.assertNotNull(cookies); Assertions.assertEquals(0, cookies.size()); } @Test public void testNoCookieSpec() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Set-Cookie", "name1=value1"); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.COOKIE_ORIGIN, this.cookieOrigin); context.setAttribute(HttpClientContext.COOKIE_SPEC, null); context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore); final HttpResponseInterceptor interceptor = ResponseProcessCookies.INSTANCE; interceptor.process(response, null, context); final List cookies = this.cookieStore.getCookies(); Assertions.assertNotNull(cookies); Assertions.assertEquals(0, cookies.size()); } @Test public void testNoCookieStore() throws Exception { final HttpResponse response = new BasicHttpResponse(200, "OK"); response.addHeader("Set-Cookie", "name1=value1"); final HttpClientContext context = HttpClientContext.create(); context.setAttribute(HttpClientContext.COOKIE_ORIGIN, this.cookieOrigin); context.setAttribute(HttpClientContext.COOKIE_SPEC, this.cookieSpec); context.setAttribute(HttpClientContext.COOKIE_STORE, null); final HttpResponseInterceptor interceptor = ResponseProcessCookies.INSTANCE; interceptor.process(response, null, context); final List cookies = this.cookieStore.getCookies(); Assertions.assertNotNull(cookies); Assertions.assertEquals(0, cookies.size()); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/psl/000077500000000000000000000000001434266521000315055ustar00rootroot00000000000000TestPublicSuffixListParser.java000066400000000000000000000074151434266521000375540ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.psl; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TestPublicSuffixListParser { @Test public void testParse() throws Exception { final ClassLoader classLoader = getClass().getClassLoader(); final InputStream in = classLoader.getResourceAsStream("suffixlist.txt"); Assertions.assertNotNull(in); final PublicSuffixList suffixList; try { final PublicSuffixListParser parser = PublicSuffixListParser.INSTANCE; suffixList = parser.parse(new InputStreamReader(in, StandardCharsets.UTF_8)); } finally { in.close(); } Assertions.assertNotNull(suffixList); Assertions.assertEquals(Arrays.asList("xx", "jp", "ac.jp", "*.tokyo.jp", "no", "h\u00E5.no"), suffixList.getRules()); Assertions.assertEquals(Collections.singletonList("metro.tokyo.jp"), suffixList.getExceptions()); } @Test public void testParseByType() throws Exception { final ClassLoader classLoader = getClass().getClassLoader(); final InputStream in = classLoader.getResourceAsStream("suffixlist2.txt"); Assertions.assertNotNull(in); final List suffixLists; try { final PublicSuffixListParser parser = PublicSuffixListParser.INSTANCE; suffixLists = parser.parseByType(new InputStreamReader(in, StandardCharsets.UTF_8)); } finally { in.close(); } Assertions.assertNotNull(suffixLists); Assertions.assertEquals(2, suffixLists.size()); final PublicSuffixList publicSuffixList1 = suffixLists.get(0); Assertions.assertNotNull(publicSuffixList1); Assertions.assertEquals(DomainType.ICANN, publicSuffixList1.getType()); Assertions.assertEquals(Arrays.asList("jp", "ac.jp", "*.tokyo.jp"), publicSuffixList1.getRules()); Assertions.assertEquals(Collections.singletonList("metro.tokyo.jp"), publicSuffixList1.getExceptions()); final PublicSuffixList publicSuffixList2 = suffixLists.get(1); Assertions.assertNotNull(publicSuffixList2); Assertions.assertEquals(DomainType.PRIVATE, publicSuffixList2.getType()); Assertions.assertEquals(Arrays.asList("googleapis.com", "googlecode.com"), publicSuffixList2.getRules()); Assertions.assertEquals(Collections.emptyList(), publicSuffixList2.getExceptions()); } } TestPublicSuffixMatcher.java000066400000000000000000000157741434266521000370560ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/psl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.psl; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TestPublicSuffixMatcher { private static final String SOURCE_FILE = "suffixlistmatcher.txt"; private PublicSuffixMatcher matcher; @BeforeEach public void setUp() throws Exception { final ClassLoader classLoader = getClass().getClassLoader(); final InputStream in = classLoader.getResourceAsStream(SOURCE_FILE); Assertions.assertNotNull(in); final List lists = PublicSuffixListParser.INSTANCE.parseByType( new InputStreamReader(in, StandardCharsets.UTF_8)); matcher = new PublicSuffixMatcher(lists); } @Test public void testGetDomainRootAnyType() { // Private Assertions.assertEquals("xx", matcher.getDomainRoot("example.XX")); Assertions.assertEquals("xx", matcher.getDomainRoot("www.example.XX")); Assertions.assertEquals("xx", matcher.getDomainRoot("www.blah.blah.example.XX")); Assertions.assertEquals("appspot.com", matcher.getDomainRoot("example.appspot.com")); // Too short Assertions.assertNull(matcher.getDomainRoot("jp")); Assertions.assertNull(matcher.getDomainRoot("ac.jp")); Assertions.assertNull(matcher.getDomainRoot("any.tokyo.jp")); // ICANN Assertions.assertEquals("metro.tokyo.jp", matcher.getDomainRoot("metro.tokyo.jp")); Assertions.assertEquals("blah.blah.tokyo.jp", matcher.getDomainRoot("blah.blah.tokyo.jp")); Assertions.assertEquals("blah.ac.jp", matcher.getDomainRoot("blah.blah.ac.jp")); // Unknown Assertions.assertEquals("garbage", matcher.getDomainRoot("garbage")); Assertions.assertEquals("garbage", matcher.getDomainRoot("garbage.garbage")); Assertions.assertEquals("garbage", matcher.getDomainRoot("*.garbage.garbage")); Assertions.assertEquals("garbage", matcher.getDomainRoot("*.garbage.garbage.garbage")); Assertions.assertEquals("*.compute-1.amazonaws.com", matcher.getDomainRoot("*.compute-1.amazonaws.com")); } @Test public void testGetDomainRootOnlyPRIVATE() { // Private Assertions.assertEquals("xx", matcher.getDomainRoot("example.XX", DomainType.PRIVATE)); Assertions.assertEquals("xx", matcher.getDomainRoot("www.example.XX", DomainType.PRIVATE)); Assertions.assertEquals("xx", matcher.getDomainRoot("www.blah.blah.example.XX", DomainType.PRIVATE)); Assertions.assertEquals("appspot.com", matcher.getDomainRoot("example.appspot.com")); // Too short Assertions.assertNull(matcher.getDomainRoot("jp", DomainType.PRIVATE)); Assertions.assertNull(matcher.getDomainRoot("ac.jp", DomainType.PRIVATE)); Assertions.assertNull(matcher.getDomainRoot("any.tokyo.jp", DomainType.PRIVATE)); // ICANN Assertions.assertNull(matcher.getDomainRoot("metro.tokyo.jp", DomainType.PRIVATE)); Assertions.assertNull(matcher.getDomainRoot("blah.blah.tokyo.jp", DomainType.PRIVATE)); Assertions.assertNull(matcher.getDomainRoot("blah.blah.ac.jp", DomainType.PRIVATE)); // Unknown Assertions.assertNull(matcher.getDomainRoot("garbage", DomainType.PRIVATE)); Assertions.assertNull(matcher.getDomainRoot("garbage.garbage", DomainType.PRIVATE)); Assertions.assertNull(matcher.getDomainRoot("*.garbage.garbage", DomainType.PRIVATE)); Assertions.assertNull(matcher.getDomainRoot("*.garbage.garbage.garbage", DomainType.PRIVATE)); } @Test public void testGetDomainRootOnlyICANN() { // Private Assertions.assertNull(matcher.getDomainRoot("example.XX", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("www.example.XX", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("www.blah.blah.example.XX", DomainType.ICANN)); // Too short Assertions.assertNull(matcher.getDomainRoot("xx", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("jp", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("ac.jp", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("any.tokyo.jp", DomainType.ICANN)); // ICANN Assertions.assertEquals("metro.tokyo.jp", matcher.getDomainRoot("metro.tokyo.jp", DomainType.ICANN)); Assertions.assertEquals("blah.blah.tokyo.jp", matcher.getDomainRoot("blah.blah.tokyo.jp", DomainType.ICANN)); Assertions.assertEquals("blah.ac.jp", matcher.getDomainRoot("blah.blah.ac.jp", DomainType.ICANN)); // Unknown Assertions.assertNull(matcher.getDomainRoot("garbage", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("garbage.garbage", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("*.garbage.garbage", DomainType.ICANN)); Assertions.assertNull(matcher.getDomainRoot("*.garbage.garbage.garbage", DomainType.ICANN)); } @Test public void testMatch() { Assertions.assertTrue(matcher.matches(".jp")); Assertions.assertTrue(matcher.matches(".ac.jp")); Assertions.assertTrue(matcher.matches(".any.tokyo.jp")); // exception Assertions.assertFalse(matcher.matches(".metro.tokyo.jp")); Assertions.assertFalse(matcher.matches(".xx")); Assertions.assertFalse(matcher.matches(".appspot.com")); } @Test public void testMatchUnicode() { Assertions.assertTrue(matcher.matches(".h\u00E5.no")); // \u00E5 is Assertions.assertTrue(matcher.matches(".xn--h-2fa.no")); Assertions.assertTrue(matcher.matches(".h\u00E5.no")); Assertions.assertTrue(matcher.matches(".xn--h-2fa.no")); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/routing/000077500000000000000000000000001434266521000323765ustar00rootroot00000000000000TestHttpRoute.java000066400000000000000000000605621434266521000357710ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/routing/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.routing; import java.net.InetAddress; import java.util.HashSet; import java.util.Set; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.RouteInfo.LayerType; import org.apache.hc.client5.http.RouteInfo.TunnelType; import org.apache.hc.core5.http.HttpHost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Tests for {@link HttpRoute}. */ public class TestHttpRoute { // a selection of constants for generating routes public final static HttpHost TARGET1 = new HttpHost("target1.test.invalid", 80); public final static HttpHost TARGET2 = new HttpHost("target2.test.invalid", 8080); // It is not necessary to have extra targets for https. // The 'layered' and 'secure' flags are specified explicitly // for routes, they will not be determined from the scheme. public final static HttpHost PROXY1 = new HttpHost("proxy1.test.invalid"); public final static HttpHost PROXY2 = new HttpHost("proxy2.test.invalid", 1080); public final static HttpHost PROXY3 = new HttpHost("proxy3.test.invalid", 88); public final static InetAddress LOCAL41; public final static InetAddress LOCAL42; public final static InetAddress LOCAL61; public final static InetAddress LOCAL62; // need static initializer to deal with exceptions static { try { LOCAL41 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 1 }); LOCAL42 = InetAddress.getByAddress(new byte[]{ 127, 0, 0, 2 }); LOCAL61 = InetAddress.getByAddress(new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }); LOCAL62 = InetAddress.getByAddress(new byte[]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }); } catch (final Exception x) { throw new ExceptionInInitializerError(x); } } @Test public void testCstrFullRoute() { // create a route with all arguments and check the details final HttpHost[] chain3 = { PROXY1, PROXY2, PROXY3 }; final HttpRoute route = new HttpRoute(TARGET1, LOCAL41, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); Assertions.assertEquals(TARGET1, route.getTargetHost(), "wrong target"); Assertions.assertEquals(LOCAL41, route.getLocalAddress(), "wrong local address"); Assertions.assertEquals(PROXY1, route.getProxyHost(), "wrong proxy host"); Assertions.assertEquals(4, route.getHopCount(), "wrong hop count"); Assertions.assertEquals(PROXY1, route.getHopTarget(0), "wrong hop 0"); Assertions.assertEquals(PROXY2, route.getHopTarget(1), "wrong hop 1"); Assertions.assertEquals(PROXY3, route.getHopTarget(2), "wrong hop 2"); Assertions.assertEquals(TARGET1, route.getHopTarget(3), "wrong hop 3"); Assertions.assertFalse(route.isSecure(), "wrong flag: secured"); Assertions.assertFalse(route.isTunnelled(), "wrong flag: tunnelled"); Assertions.assertFalse(route.isLayered(), "wrong flag: layered"); final String routestr = route.toString(); Assertions.assertTrue(routestr.contains(TARGET1.getHostName()), "missing target in toString"); Assertions.assertTrue(routestr.contains(LOCAL41.toString()), "missing local address in toString"); Assertions.assertTrue(routestr.contains(PROXY1.getHostName()), "missing proxy 1 in toString"); Assertions.assertTrue(routestr.contains(PROXY2.getHostName()), "missing proxy 2 in toString"); Assertions.assertTrue(routestr.contains(PROXY3.getHostName()), "missing proxy 3 in toString"); } @Test public void testCstrFullFlags() { // tests the flag parameters in the full-blown constructor final HttpHost[] chain3 = { PROXY1, PROXY2, PROXY3 }; final HttpRoute routefff = new HttpRoute (TARGET1, LOCAL41, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute routefft = new HttpRoute (TARGET1, LOCAL41, chain3, false, TunnelType.PLAIN, LayerType.LAYERED); final HttpRoute routeftf = new HttpRoute (TARGET1, LOCAL41, chain3, false, TunnelType.TUNNELLED, LayerType.PLAIN); final HttpRoute routeftt = new HttpRoute (TARGET1, LOCAL41, chain3, false, TunnelType.TUNNELLED, LayerType.LAYERED); final HttpRoute routetff = new HttpRoute (TARGET1, LOCAL41, chain3, true, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute routetft = new HttpRoute (TARGET1, LOCAL41, chain3, true, TunnelType.PLAIN, LayerType.LAYERED); final HttpRoute routettf = new HttpRoute (TARGET1, LOCAL41, chain3, true, TunnelType.TUNNELLED, LayerType.PLAIN); final HttpRoute routettt = new HttpRoute (TARGET1, LOCAL41, chain3, true, TunnelType.TUNNELLED, LayerType.LAYERED); Assertions.assertFalse(routefff.isSecure(), "routefff.secure"); Assertions.assertFalse(routefff.isTunnelled(), "routefff.tunnel"); Assertions.assertFalse(routefff.isLayered(), "routefff.layer"); Assertions.assertFalse(routefft.isSecure(), "routefft.secure"); Assertions.assertFalse(routefft.isTunnelled(), "routefft.tunnel"); Assertions.assertTrue (routefft.isLayered(), "routefft.layer"); Assertions.assertFalse(routeftf.isSecure(), "routeftf.secure"); Assertions.assertTrue (routeftf.isTunnelled(), "routeftf.tunnel"); Assertions.assertFalse(routeftf.isLayered(), "routeftf.layer"); Assertions.assertFalse(routeftt.isSecure(), "routeftt.secure"); Assertions.assertTrue (routeftt.isTunnelled(), "routeftt.tunnel"); Assertions.assertTrue (routeftt.isLayered(), "routeftt.layer"); Assertions.assertTrue (routetff.isSecure(), "routetff.secure"); Assertions.assertFalse(routetff.isTunnelled(), "routetff.tunnel"); Assertions.assertFalse(routetff.isLayered(), "routetff.layer"); Assertions.assertTrue (routetft.isSecure(), "routetft.secure"); Assertions.assertFalse(routetft.isTunnelled(), "routetft.tunnel"); Assertions.assertTrue (routetft.isLayered(), "routetft.layer"); Assertions.assertTrue (routettf.isSecure(), "routettf.secure"); Assertions.assertTrue (routettf.isTunnelled(), "routettf.tunnel"); Assertions.assertFalse(routettf.isLayered(), "routettf.layer"); Assertions.assertTrue (routettt.isSecure(), "routettt.secure"); Assertions.assertTrue (routettt.isTunnelled(), "routettt.tunnel"); Assertions.assertTrue (routettt.isLayered(), "routettt.layer"); final Set routes = new HashSet<>(); routes.add(routefff); routes.add(routefft); routes.add(routeftf); routes.add(routeftt); routes.add(routetff); routes.add(routetft); routes.add(routettf); routes.add(routettt); Assertions.assertEquals(8, routes.size(), "some flagged routes are equal"); // we can't test hashCode in general due to its dependency // on InetAddress and HttpHost, but we can check for the flags final Set routecodes = new HashSet<>(); routecodes.add(routefff.hashCode()); routecodes.add(routefft.hashCode()); routecodes.add(routeftf.hashCode()); routecodes.add(routeftt.hashCode()); routecodes.add(routetff.hashCode()); routecodes.add(routetft.hashCode()); routecodes.add(routettf.hashCode()); routecodes.add(routettt.hashCode()); Assertions.assertEquals(8, routecodes.size(), "some flagged routes have same hashCode"); final Set routestrings = new HashSet<>(); routestrings.add(routefff.toString()); routestrings.add(routefft.toString()); routestrings.add(routeftf.toString()); routestrings.add(routeftt.toString()); routestrings.add(routetff.toString()); routestrings.add(routetft.toString()); routestrings.add(routettf.toString()); routestrings.add(routettt.toString()); Assertions.assertEquals(8, routestrings.size(), "some flagged route.toString() are equal"); } @SuppressWarnings("unused") @Test public void testInvalidArguments() { final HttpHost[] chain1 = { PROXY1 }; // for reference: this one should succeed final HttpRoute route = new HttpRoute(TARGET1, null, chain1, false, TunnelType.TUNNELLED, LayerType.PLAIN); Assertions.assertNotNull(route); Assertions.assertThrows(NullPointerException.class, () -> new HttpRoute(null, null, chain1, false,TunnelType.TUNNELLED, LayerType.PLAIN)); Assertions.assertThrows(IllegalArgumentException.class, () -> new HttpRoute(TARGET1, null, (HttpHost[]) null, false, TunnelType.TUNNELLED, LayerType.PLAIN)); } @Test public void testNullEnums() { // tests the default values for the enum parameters // also covers the accessors for the enum attributes final HttpRoute route = new HttpRoute(TARGET1, null, PROXY1, false, null, null); // here are defaults Assertions.assertFalse(route.isTunnelled(), "default tunnelling"); Assertions.assertEquals(TunnelType.PLAIN, route.getTunnelType(), "untunnelled"); Assertions.assertFalse(route.isLayered(), "default layering"); Assertions.assertEquals(LayerType.PLAIN, route.getLayerType(), "unlayered"); } @Test public void testEqualsHashcodeClone() throws CloneNotSupportedException { final HttpHost[] chain0 = { }; final HttpHost[] chain1 = { PROXY1 }; final HttpHost[] chain3 = { PROXY1, PROXY2, PROXY3 }; final HttpHost[] chain4 = { PROXY1, PROXY3, PROXY2 }; // create some identical routes final HttpRoute route1a = new HttpRoute(TARGET1, LOCAL41, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route1b = new HttpRoute(TARGET1, LOCAL41, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route1c = (HttpRoute) route1a.clone(); Assertions.assertEquals(route1a, route1a, "1a 1a"); Assertions.assertEquals(route1a, route1b, "1a 1b"); Assertions.assertEquals(route1a, route1c, "1a 1c"); Assertions.assertEquals(route1a.hashCode(), route1a.hashCode(), "hashcode 1a"); Assertions.assertEquals(route1a.hashCode(), route1b.hashCode(), "hashcode 1b"); Assertions.assertEquals(route1a.hashCode(), route1c.hashCode(), "hashcode 1c"); Assertions.assertEquals(route1a.toString(), route1b.toString(), "toString 1b"); Assertions.assertEquals(route1a.toString(), route1a.toString(), "toString 1a"); Assertions.assertEquals(route1a.toString(), route1c.toString(), "toString 1c"); // now create some differing routes final HttpRoute route2a = new HttpRoute(TARGET2, LOCAL41, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2b = new HttpRoute(TARGET1, LOCAL42, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2c = new HttpRoute(TARGET1, LOCAL61, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2d = new HttpRoute(TARGET1, null, chain3, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2e = new HttpRoute(TARGET1, LOCAL41, (HttpHost[]) null, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2f = new HttpRoute(TARGET1, LOCAL41, chain0, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2g = new HttpRoute(TARGET1, LOCAL41, chain1, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2h = new HttpRoute(TARGET1, LOCAL41, chain4, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2i = new HttpRoute(TARGET1, LOCAL41, chain3, true, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2j = new HttpRoute(TARGET1, LOCAL41, chain3, false, TunnelType.TUNNELLED, LayerType.PLAIN); final HttpRoute route2k = new HttpRoute(TARGET1, LOCAL41, chain3, false, TunnelType.PLAIN, LayerType.LAYERED); // check a special case first: 2f should be the same as 2e Assertions.assertEquals(route2e, route2f, "2e 2f"); Assertions.assertEquals(route2e.hashCode(), route2f.hashCode(), "hashcode 2e 2f"); Assertions.assertEquals(route2e.toString(), route2f.toString(), "toString 2e 2f"); Assertions.assertNotEquals(route1a, route2a, "1a 2a"); Assertions.assertNotEquals(route1a, route2b, "1a 2b"); Assertions.assertNotEquals(route1a, route2c, "1a 2c"); Assertions.assertNotEquals(route1a, route2d, "1a 2d"); Assertions.assertNotEquals(route1a, route2e, "1a 2e"); Assertions.assertNotEquals(route1a, route2f, "1a 2f"); Assertions.assertNotEquals(route1a, route2g, "1a 2g"); Assertions.assertNotEquals(route1a, route2h, "1a 2h"); Assertions.assertNotEquals(route1a, route2i, "1a 2i"); Assertions.assertNotEquals(route1a, route2j, "1a 2j"); Assertions.assertNotEquals(route1a, route2k, "1a 2k"); // repeat the checks in the other direction // there could be problems with detecting null attributes Assertions.assertNotEquals(route2b, route1a, "2b 1a"); Assertions.assertNotEquals(route2c, route1a, "2c 1a"); Assertions.assertNotEquals(route2d, route1a, "2d 1a"); Assertions.assertNotEquals(route2e, route1a, "2e 1a"); Assertions.assertNotEquals(route2a, route1a, "2a 1a"); Assertions.assertNotEquals(route2f, route1a, "2f 1a"); Assertions.assertNotEquals(route2g, route1a, "2g 1a"); Assertions.assertNotEquals(route2h, route1a, "2h 1a"); Assertions.assertNotEquals(route2i, route1a, "2i 1a"); Assertions.assertNotEquals(route2j, route1a, "2j 1a"); Assertions.assertNotEquals(route2k, route1a, "2k 1a"); // don't check hashCode, it's not guaranteed to be different Assertions.assertNotEquals(route1a.toString(), route2a.toString(), "toString 1a 2a"); Assertions.assertNotEquals(route1a.toString(), route2b.toString(), "toString 1a 2b"); Assertions.assertNotEquals(route1a.toString(), route2c.toString(), "toString 1a 2c"); Assertions.assertNotEquals(route1a.toString(), route2d.toString(), "toString 1a 2d"); Assertions.assertNotEquals(route1a.toString(), route2e.toString(), "toString 1a 2e"); Assertions.assertNotEquals(route1a.toString(), route2f.toString(), "toString 1a 2f"); Assertions.assertNotEquals(route1a.toString(), route2g.toString(), "toString 1a 2g"); Assertions.assertNotEquals(route1a.toString(), route2h.toString(), "toString 1a 2h"); Assertions.assertNotEquals(route1a.toString(), route2i.toString(), "toString 1a 2i"); Assertions.assertNotEquals(route1a.toString(), route2j.toString(), "toString 1a 2j"); Assertions.assertNotEquals(route1a.toString(), route2k.toString(), "toString 1a 2k"); // now check that all of the routes are different from eachother // except for those that aren't :-) final Set routes = new HashSet<>(); routes.add(route1a); routes.add(route2a); routes.add(route2b); routes.add(route2c); routes.add(route2d); routes.add(route2e); //routes.add(route2f); // 2f is the same as 2e routes.add(route2g); routes.add(route2h); routes.add(route2i); routes.add(route2j); routes.add(route2k); Assertions.assertEquals(11, routes.size(), "some routes are equal"); // and a run of cloning over the set for (final HttpRoute origin : routes) { final HttpRoute cloned = (HttpRoute) origin.clone(); Assertions.assertEquals(origin, cloned, "clone of " + origin); Assertions.assertTrue(routes.contains(cloned), "clone of " + origin); } // and don't forget toString final Set routestrings = new HashSet<>(); routestrings.add(route1a.toString()); routestrings.add(route2a.toString()); routestrings.add(route2b.toString()); routestrings.add(route2c.toString()); routestrings.add(route2d.toString()); routestrings.add(route2e.toString()); //routestrings.add(route2f.toString()); // 2f is the same as 2e routestrings.add(route2g.toString()); routestrings.add(route2h.toString()); routestrings.add(route2i.toString()); routestrings.add(route2j.toString()); routestrings.add(route2k.toString()); Assertions.assertEquals(11, routestrings.size(), "some route.toString() are equal"); // finally, compare with nonsense Assertions.assertNotEquals(null, route1a, "route equals null"); Assertions.assertNotEquals("route1a", route1a, "route equals string"); } @Test public void testHopping() { // test getHopCount() and getHopTarget() with different proxy chains final HttpHost[] proxies = null; final HttpRoute route = new HttpRoute(TARGET1, null, proxies, true, TunnelType.PLAIN, LayerType.PLAIN); Assertions.assertEquals(1, route.getHopCount(), "A: hop count"); Assertions.assertEquals(TARGET1, route.getHopTarget(0), "A: hop 0"); Assertions.assertThrows(IllegalArgumentException.class, () -> route.getHopTarget(1)); Assertions.assertThrows(IllegalArgumentException.class, () -> route.getHopTarget(-1)); final HttpHost[] proxies2 = new HttpHost[]{ PROXY3 }; final HttpRoute route2 = new HttpRoute(TARGET1, LOCAL62, proxies2, false, TunnelType.TUNNELLED, LayerType.PLAIN); Assertions.assertEquals(2, route2.getHopCount(), "B: hop count"); Assertions.assertEquals(PROXY3, route2.getHopTarget(0), "B: hop 0"); Assertions.assertEquals(TARGET1, route2.getHopTarget(1), "B: hop 1"); Assertions.assertThrows(IllegalArgumentException.class, () -> route2.getHopTarget(2)); Assertions.assertThrows(IllegalArgumentException.class, () -> route2.getHopTarget(-2)); final HttpHost[] proxies3 = new HttpHost[]{ PROXY3, PROXY1, PROXY2 }; final HttpRoute route3 = new HttpRoute(TARGET1, LOCAL42, proxies3, false, TunnelType.PLAIN, LayerType.LAYERED); Assertions.assertEquals(route3.getHopCount(), 4, "C: hop count"); Assertions.assertEquals(PROXY3 , route3.getHopTarget(0), "C: hop 0"); Assertions.assertEquals(PROXY1 , route3.getHopTarget(1), "C: hop 1"); Assertions.assertEquals(PROXY2 , route3.getHopTarget(2), "C: hop 2"); Assertions.assertEquals(TARGET1, route3.getHopTarget(3), "C: hop 3"); Assertions.assertThrows(IllegalArgumentException.class, () -> route3.getHopTarget(4)); Assertions.assertThrows(IllegalArgumentException.class, () -> route3.getHopTarget(Integer.MIN_VALUE)); } @Test public void testCstr1() { final HttpRoute route = new HttpRoute(TARGET2); final HttpRoute should = new HttpRoute (TARGET2, null, (HttpHost[]) null, false, TunnelType.PLAIN, LayerType.PLAIN); Assertions.assertEquals(route, should, "bad convenience route"); } @Test public void testCstr3() { // test convenience constructor with 3 arguments HttpRoute route = new HttpRoute(TARGET2, LOCAL61, false); HttpRoute should = new HttpRoute (TARGET2, LOCAL61, (HttpHost[]) null, false, TunnelType.PLAIN, LayerType.PLAIN); Assertions.assertEquals(route, should, "bad convenience route 3/insecure"); route = new HttpRoute(TARGET2, null, true); should = new HttpRoute(TARGET2, null, (HttpHost[]) null, true, TunnelType.PLAIN, LayerType.PLAIN); Assertions.assertEquals(route, should, "bad convenience route 3/secure"); } @SuppressWarnings("unused") @Test public void testCstr4() { // test convenience constructor with 4 arguments HttpRoute route = new HttpRoute(TARGET2, null, PROXY2, false); HttpRoute should = new HttpRoute (TARGET2, null, new HttpHost[]{ PROXY2 }, false, TunnelType.PLAIN, LayerType.PLAIN); Assertions.assertEquals(route, should, "bad convenience route 4/insecure"); route = new HttpRoute(TARGET2, LOCAL42, PROXY1, true); should = new HttpRoute (TARGET2, LOCAL42, new HttpHost[]{ PROXY1 }, true, TunnelType.TUNNELLED, LayerType.LAYERED); Assertions.assertEquals(route, should, "bad convenience route 4/secure"); // this constructor REQUIRES a proxy to be specified Assertions.assertThrows(NullPointerException.class, () -> new HttpRoute(TARGET1, LOCAL61, null, false)); } @Test public void testCstr6() { // test convenience constructor with 6 arguments HttpRoute route = new HttpRoute (TARGET2, null, PROXY2, true, TunnelType.TUNNELLED, LayerType.PLAIN); HttpRoute should = new HttpRoute (TARGET2, null, new HttpHost[]{ PROXY2 }, true, TunnelType.TUNNELLED, LayerType.PLAIN); Assertions.assertEquals(route, should, "bad convenience route 6/proxied"); route = new HttpRoute (TARGET2, null, (HttpHost) null, true, TunnelType.PLAIN, LayerType.LAYERED); should = new HttpRoute (TARGET2, null, (HttpHost[]) null, true, TunnelType.PLAIN, LayerType.LAYERED); Assertions.assertEquals(route, should, "bad convenience route 6/direct"); // handling of null vs. empty chain is checked in the equals tests } @Test public void testImmutable() throws CloneNotSupportedException { final HttpHost[] proxies = new HttpHost[]{ PROXY1, PROXY2, PROXY3 }; final HttpRoute route1 = new HttpRoute(TARGET1, null, proxies, false, TunnelType.PLAIN, LayerType.PLAIN); final HttpRoute route2 = (HttpRoute) route1.clone(); final HttpRoute route3 = new HttpRoute(TARGET1, null, proxies.clone(), false, TunnelType.PLAIN, LayerType.PLAIN); // modify the array that was passed to the constructor of route1 proxies[1] = PROXY3; proxies[2] = PROXY2; Assertions.assertEquals(route2, route1, "route differs from clone"); Assertions.assertEquals(route3, route1, "route was modified"); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/000077500000000000000000000000001434266521000315105ustar00rootroot00000000000000CertificatesToPlayWith.java000066400000000000000000001124111434266521000366660ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; /** * Some X509 certificates to test against. *

* Note: some of these certificates have Japanese Kanji in the "subjectAlt" * field (UTF8). Not sure how realistic that is since international characters * in DNS names usually get translated into ASCII using "xn--" style DNS * entries. "xn--i8s592g.co.jp" is what FireFox actually uses when trying to * find 花子.co.jp. So would the CN in the certificate contain * "xn--i8s592g.co.jp" in ASCII, or "花子.co.jp" in UTF8? (Both?) *

* * @since 11-Dec-2006 */ public class CertificatesToPlayWith { /** * CN=foo.com */ public final static byte[] X509_FOO = ( "-----BEGIN CERTIFICATE-----\n" + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aQMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzE0MVoXDTI4MTEwNTE1MzE0MVowgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQC3jRmEya6sQCkmieULcvx8zz1euCk9\n" + "fSez7BEtki8+dmfMXe3K7sH0lI8f4jJR0rbSCjpmCQLYmzC3NxBKeJOW0RcjNBpO\n" + "c2JlGO9auXv2GDP4IYiXElLJ6VSqc8WvDikv0JmCCWm0Zga+bZbR/EWN5DeEtFdF\n" + "815CLpJZNcYwiYwGy/CVQ7w2TnXlG+mraZOz+owr+cL6J/ZesbdEWfjoS1+cUEhE\n" + "HwlNrAu8jlZ2UqSgskSWlhYdMTAP9CPHiUv9N7FcT58Itv/I4fKREINQYjDpvQcx\n" + "SaTYb9dr5sB4WLNglk7zxDtM80H518VvihTcP7FHL+Gn6g4j5fkI98+S\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=花子.co.jp */ public final static byte[] X509_HANAKO = ( "-----BEGIN CERTIFICATE-----\n" + "MIIESzCCAzOgAwIBAgIJAIz+EYMBU6aTMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1NDIxNVoXDTI4MTEwNTE1NDIxNVowgakx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + "cnRpZmljYXRlczEVMBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkB\n" + "FhZqdWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + "MIIBCgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjU\n" + "g4pNjYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQc\n" + "wHf0ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t\n" + "7iu1JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAn\n" + "AxK6q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArD\n" + "qUYxqJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n" + "CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n" + "HQ4EFgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLS\n" + "rNuzA1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBALJ27i3okV/KvlDp6KMID3gd\n" + "ITl68PyItzzx+SquF8gahMh016NX73z/oVZoVUNdftla8wPUB1GwIkAnGkhQ9LHK\n" + "spBdbRiCj0gMmLCsX8SrjFvr7cYb2cK6J/fJe92l1tg/7Y4o7V/s4JBe/cy9U9w8\n" + "a0ctuDmEBCgC784JMDtT67klRfr/2LlqWhlOEq7pUFxRLbhpquaAHSOjmIcWnVpw\n" + "9BsO7qe46hidgn39hKh1WjKK2VcL/3YRsC4wUi0PBtFW6ScMCuMhgIRXSPU55Rae\n" + "UIlOdPjjr1SUNWGId1rD7W16Scpwnknn310FNxFMHVI0GTGFkNdkilNCFJcIoRA=\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=foo.com, subjectAlt=bar.com */ public final static byte[] X509_FOO_BAR = ( "-----BEGIN CERTIFICATE-----\n" + "MIIEXDCCA0SgAwIBAgIJAIz+EYMBU6aRMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzYyOVoXDTI4MTEwNTE1MzYyOVowgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCG\n" + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + "A1LKh6YNPg0wEgYDVR0RBAswCYIHYmFyLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA\n" + "dQyprNZBmVnvuVWjV42sey/PTfkYShJwy1j0/jcFZR/ypZUovpiHGDO1DgL3Y3IP\n" + "zVQ26uhUsSw6G0gGRiaBDe/0LUclXZoJzXX1qpS55OadxW73brziS0sxRgGrZE/d\n" + "3g5kkio6IED47OP6wYnlmZ7EKP9cqjWwlnvHnnUcZ2SscoLNYs9rN9ccp8tuq2by\n" + "88OyhKwGjJfhOudqfTNZcDzRHx4Fzm7UsVaycVw4uDmhEHJrAsmMPpj/+XRK9/42\n" + "2xq+8bc6HojdtbCyug/fvBZvZqQXSmU8m8IVcMmWMz0ZQO8ee3QkBHMZfCy7P/kr\n" + "VbWx/uETImUu+NZg22ewEw==\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=foo.com, subjectAlt=bar.com, subjectAlt=花子.co.jp * (hanako.co.jp in kanji) */ public final static byte[] X509_FOO_BAR_HANAKO = ( "-----BEGIN CERTIFICATE-----\n" + "MIIEajCCA1KgAwIBAgIJAIz+EYMBU6aSMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzgxM1oXDTI4MTEwNTE1MzgxM1owgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBnjCBmzAJBgNVHRMEAjAAMCwGCWCG\n" + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + "A1LKh6YNPg0wIAYDVR0RBBkwF4IHYmFyLmNvbYIM6Iqx5a2QLmNvLmpwMA0GCSqG\n" + "SIb3DQEBBQUAA4IBAQBeZs7ZIYyKtdnVxVvdLgwySEPOE4pBSXii7XYv0Q9QUvG/\n" + "++gFGQh89HhABzA1mVUjH5dJTQqSLFvRfqTHqLpxSxSWqMHnvRM4cPBkIRp/XlMK\n" + "PlXadYtJLPTgpbgvulA1ickC9EwlNYWnowZ4uxnfsMghW4HskBqaV+PnQ8Zvy3L0\n" + "12c7Cg4mKKS5pb1HdRuiD2opZ+Hc77gRQLvtWNS8jQvd/iTbh6fuvTKfAOFoXw22\n" + "sWIKHYrmhCIRshUNohGXv50m2o+1w9oWmQ6Dkq7lCjfXfUB4wIbggJjpyEtbNqBt\n" + "j4MC2x5rfsLKKqToKmNE7pFEgqwe8//Aar1b+Qj+\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=*.foo.com */ public final static byte[] X509_WILD_FOO = ( "-----BEGIN CERTIFICATE-----\n" + "MIIESDCCAzCgAwIBAgIJAIz+EYMBU6aUMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTU1NVoXDTI4MTEwNTE2MTU1NVowgaYx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCG\n" + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + "A1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBAH0ipG6J561UKUfgkeW7GvYwW98B\n" + "N1ZooWX+JEEZK7+Pf/96d3Ij0rw9ACfN4bpfnCq0VUNZVSYB+GthQ2zYuz7tf/UY\n" + "A6nxVgR/IjG69BmsBl92uFO7JTNtHztuiPqBn59pt+vNx4yPvno7zmxsfI7jv0ww\n" + "yfs+0FNm7FwdsC1k47GBSOaGw38kuIVWqXSAbL4EX9GkryGGOKGNh0qvAENCdRSB\n" + "G9Z6tyMbmfRY+dLSh3a9JwoEcBUso6EWYBakLbq4nG/nvYdYvG9ehrnLVwZFL82e\n" + "l3Q/RK95bnA6cuRClGusLad0e6bjkBzx/VQ3VarDEpAkTLUGVAa0CLXtnyc=\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=*.co.jp */ public final static byte[] X509_WILD_CO_JP = ( "-----BEGIN CERTIFICATE-----\n" + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aVMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTYzMFoXDTI4MTEwNTE2MTYzMFowgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxQHKi5jby5qcDElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQA0sWglVlMx2zNGvUqFC73XtREwii53\n" + "CfMM6mtf2+f3k/d8KXhLNySrg8RRlN11zgmpPaLtbdTLrmG4UdAHHYr8O4y2BBmE\n" + "1cxNfGxxechgF8HX10QV4dkyzp6Z1cfwvCeMrT5G/V1pejago0ayXx+GPLbWlNeZ\n" + "S+Kl0m3p+QplXujtwG5fYcIpaGpiYraBLx3Tadih39QN65CnAh/zRDhLCUzKyt9l\n" + "UGPLEUDzRHMPHLnSqT1n5UU5UDRytbjJPXzF+l/+WZIsanefWLsxnkgAuZe/oMMF\n" + "EJMryEzOjg4Tfuc5qM0EXoPcQ/JlheaxZ40p2IyHqbsWV4MRYuFH4bkM\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=*.foo.com, subjectAlt=*.bar.com, subjectAlt=*.花子.co.jp * (*.hanako.co.jp in kanji) */ public final static byte[] X509_WILD_FOO_BAR_HANAKO = ( "-----BEGIN CERTIFICATE-----\n" + "MIIEcDCCA1igAwIBAgIJAIz+EYMBU6aWMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTczMVoXDTI4MTEwNTE2MTczMVowgaYx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo4GiMIGfMAkGA1UdEwQCMAAwLAYJ\n" + "YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud\n" + "DgQWBBSfFHe/Pzq2yjiCQkgWLNrQy16H2DAfBgNVHSMEGDAWgBR7mtqPkJlOUtKs\n" + "27MDUsqHpg0+DTAkBgNVHREEHTAbggkqLmJhci5jb22CDiou6Iqx5a2QLmNvLmpw\n" + "MA0GCSqGSIb3DQEBBQUAA4IBAQBobWC+D5/lx6YhX64CwZ26XLjxaE0S415ajbBq\n" + "DK7lz+Rg7zOE3GsTAMi+ldUYnhyz0wDiXB8UwKXl0SDToB2Z4GOgqQjAqoMmrP0u\n" + "WB6Y6dpkfd1qDRUzI120zPYgSdsXjHW9q2H77iV238hqIU7qCvEz+lfqqWEY504z\n" + "hYNlknbUnR525ItosEVwXFBJTkZ3Yw8gg02c19yi8TAh5Li3Ad8XQmmSJMWBV4XK\n" + "qFr0AIZKBlg6NZZFf/0dP9zcKhzSriW27bY0XfzA6GSiRDXrDjgXq6baRT6YwgIg\n" + "pgJsDbJtZfHnV1nd3M6zOtQPm1TIQpNmMMMd/DPrGcUQerD3\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=foo.com, CN=bar.com, CN=花子.co.jp */ public final static byte[] X509_THREE_CNS_FOO_BAR_HANAKO = ( "-----BEGIN CERTIFICATE-----\n" + "MIIEbzCCA1egAwIBAgIJAIz+EYMBU6aXMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTk0NVoXDTI4MTEwNTE2MTk0NVowgc0x\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAwwHZm9vLmNvbTEQMA4GA1UEAwwHYmFyLmNvbTEV\n" + "MBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGOv\n" + "loI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pNjYGViGjg7zhf\n" + "bjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0ZHLN6sD9m2uV\n" + "Sp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1JVjTuE0pcBva\n" + "h2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6q/wGqcZ3zvFB\n" + "TcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYxqJUlPGlMqrKb\n" + "3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf\n" + "Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86\n" + "tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0w\n" + "DQYJKoZIhvcNAQEFBQADggEBAGuZb8ai1NO2j4v3y9TLZvd5s0vh5/TE7n7RX+8U\n" + "y37OL5k7x9nt0mM1TyAKxlCcY+9h6frue8MemZIILSIvMrtzccqNz0V1WKgA+Orf\n" + "uUrabmn+CxHF5gpy6g1Qs2IjVYWA5f7FROn/J+Ad8gJYc1azOWCLQqSyfpNRLSvY\n" + "EriQFEV63XvkJ8JrG62b+2OT2lqT4OO07gSPetppdlSa8NBSKP6Aro9RIX1ZjUZQ\n" + "SpQFCfo02NO0uNRDPUdJx2huycdNb+AXHaO7eXevDLJ+QnqImIzxWiY6zLOdzjjI\n" + "VBMkLHmnP7SjGSQ3XA4ByrQOxfOUTyLyE7NuemhHppuQPxE=\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * subjectAlt=foo.com */ public final static byte[] X509_NO_CNS_FOO = ( "-----BEGIN CERTIFICATE-----\n" + "MIIESjCCAzKgAwIBAgIJAIz+EYMBU6aYMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MjYxMFoXDTI4MTEwNTE2MjYxMFowgZIx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + "cnRpZmljYXRlczElMCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNv\n" + "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhjr5aCPoyp0R1iroWA\n" + "fnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2BlYho4O84X244QrZTRl8kQbYt\n" + "xnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRyzerA/ZtrlUqf+lKo0uWcocxe\n" + "Rc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY07hNKXAb2odnVqgzcYiDkLV8\n" + "ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8BqnGd87xQU3FVZI4tbtkB+Kz\n" + "jD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiVJTxpTKqym93whYk93l3ocEe5\n" + "5c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM\n" + "IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86tso4gkJIFiza\n" + "0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0wEgYDVR0RBAsw\n" + "CYIHZm9vLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAjl78oMjzFdsMy6F1sGg/IkO8\n" + "tF5yUgPgFYrs41yzAca7IQu6G9qtFDJz/7ehh/9HoG+oqCCIHPuIOmS7Sd0wnkyJ\n" + "Y7Y04jVXIb3a6f6AgBkEFP1nOT0z6kjT7vkA5LJ2y3MiDcXuRNMSta5PYVnrX8aZ\n" + "yiqVUNi40peuZ2R8mAUSBvWgD7z2qWhF8YgDb7wWaFjg53I36vWKn90ZEti3wNCw\n" + "qAVqixM+J0qJmQStgAc53i2aTMvAQu3A3snvH/PHTBo+5UL72n9S1kZyNCsVf1Qo\n" + "n8jKTiRriEM+fMFlcgQP284EBFzYHyCXFb9O/hMjK2+6mY9euMB1U1aFFzM/Bg==\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * Intermediate CA for all of these. */ public final static byte[] X509_INTERMEDIATE_CA = ( "-----BEGIN CERTIFICATE-----\n" + "MIIEnDCCA4SgAwIBAgIJAJTNwZ6yNa5cMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDAS\n" + "BgNVBAsUC2NvbW1vbnNfc3NsMRUwEwYDVQQDFAxkZW1vX3Jvb3RfY2ExJTAjBgkq\n" + "hkiG9w0BCQEWFmp1bGl1c2Rhdmllc0BnbWFpbC5jb20wHhcNMDYxMTA1MjE0OTMx\n" + "WhcNMDcxMTA1MjE0OTMxWjCBojELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRIw\n" + "EAYDVQQHEwlWYW5jb3V2ZXIxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDASBgNV\n" + "BAsUC2NvbW1vbnNfc3NsMR0wGwYDVQQDFBRkZW1vX2ludGVybWVkaWF0ZV9jYTEl\n" + "MCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZI\n" + "hvcNAQEBBQADggEPADCCAQoCggEBAL0S4y3vUO0EM6lwqOEfK8fvrUprIbsikXaG\n" + "XzejcZ+T3l2Dc7t8WtBfRf78i4JypMqJQSijrUicj3H6mOMIReKaXm6ls4hA5d8w\n" + "Lhmgiqsz/kW+gA8SeWGWRN683BD/RbQmzOls6ynBvap9jZlthXWBrSIlPCQoBLXY\n" + "KVaxGzbL4ezaq+XFMKMQSm2uKwVmHHQNbfmZlPsuendBVomb/ked53Ab9IH6dwwN\n" + "qJH9WIrvIzIVEXWlpvQ5MCqozM7u1akU+G8cazr8theGPCaYkzoXnigWua4OjdpV\n" + "9z5ZDknhfBzG1AjapdG07FIirwWWgIyZXqZSD96ikmLtwT29qnsCAwEAAaOB7jCB\n" + "6zAdBgNVHQ4EFgQUe5raj5CZTlLSrNuzA1LKh6YNPg0wgbsGA1UdIwSBszCBsIAU\n" + "rN8eFIvMiRFXXgDqKumS0/W2AhOhgYykgYkwgYYxCzAJBgNVBAYTAkNBMQswCQYD\n" + "VQQIEwJCQzEWMBQGA1UEChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9u\n" + "c19zc2wxFTATBgNVBAMUDGRlbW9fcm9vdF9jYTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbYIJAJTNwZ6yNa5bMAwGA1UdEwQFMAMBAf8wDQYJ\n" + "KoZIhvcNAQEFBQADggEBAIB4KMZvHD20pdKajFtMBpL7X4W4soq6EeTtjml3NYa9\n" + "Qc52bsQEGNccKY9afYSBIndaQvFdtmz6HdoN+B8TjYShw2KhyjtKimGLpWYoi1YF\n" + "e4aHdmA/Gp5xk8pZzR18FmooxC9RqBux+NAM2iTFSLgDtGIIj4sg2rbn6Bb6ZlQT\n" + "1rg6VucXCA1629lNfMeNcu7CBNmUKIdaxHR/YJQallE0KfGRiOIWPrPj/VNk0YA6\n" + "XFg0ocjqXJ2/N0N9rWVshMUaXgOh7m4D/5zga5/nuxDU+PoToA6mQ4bV6eCYqZbh\n" + "aa1kQYtR9B4ZiG6pB82qVc2dCqStOH2FAEWos2gAVkQ=\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * Root CA for all of these. */ public final static byte[] X509_ROOT_CA = ( "-----BEGIN CERTIFICATE-----\n" + "MIIEgDCCA2igAwIBAgIJAJTNwZ6yNa5bMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDAS\n" + "BgNVBAsUC2NvbW1vbnNfc3NsMRUwEwYDVQQDFAxkZW1vX3Jvb3RfY2ExJTAjBgkq\n" + "hkiG9w0BCQEWFmp1bGl1c2Rhdmllc0BnbWFpbC5jb20wHhcNMDYxMTA1MjEzNjQz\n" + "WhcNMjYxMTA1MjEzNjQzWjCBhjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRYw\n" + "FAYDVQQKEw13d3cuY3VjYmMuY29tMRQwEgYDVQQLFAtjb21tb25zX3NzbDEVMBMG\n" + "A1UEAxQMZGVtb19yb290X2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZpZXNA\n" + "Z21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv+OnocmJ\n" + "79UeO2hlCwK+Cle5uZWnU6uwJl+08z5cvebb5tT64WL9+psDbfgUH/Gm9JsuxKTg\n" + "w1tZO/4duIgnaLNSx4HoqaTjwigd/hR3TsoGEPXTCkz1ikgTCOEDvl+iMid6aOrd\n" + "mViE8HhscxKZ+h5FE7oHZyuT6gFoiaIXhFq+xK2w4ZwDz9L+paiwqywyUJJMnh9U\n" + "jKorY+nua81N0oxpIhHPspCanDU4neMzCzYOZyLR/LqV5xORvHcFY84GWMz5hI25\n" + "JbgaWJsYKuCAvNsnQwVoqKPGa7x1fn7x6oGsXJaCVt8weUwIj2xwg1lxMhrNaisH\n" + "EvKpEAEnGGwWKQIDAQABo4HuMIHrMB0GA1UdDgQWBBSs3x4Ui8yJEVdeAOoq6ZLT\n" + "9bYCEzCBuwYDVR0jBIGzMIGwgBSs3x4Ui8yJEVdeAOoq6ZLT9bYCE6GBjKSBiTCB\n" + "hjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRYwFAYDVQQKEw13d3cuY3VjYmMu\n" + "Y29tMRQwEgYDVQQLFAtjb21tb25zX3NzbDEVMBMGA1UEAxQMZGVtb19yb290X2Nh\n" + "MSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZpZXNAZ21haWwuY29tggkAlM3BnrI1\n" + "rlswDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAlPl3/8h1LttR1svC\n" + "S8RXbHpAWIT2BEDhGHUNjSmgDQNkE/itf/FCEXh0tlU4bYdtBSOHzflbnzOyIPId\n" + "VZeSWs33V38xDFy6KoVg1gT8JxkLmE5S1vWkpsHIlpw/U6r7KD0Kx9FYx5AiXjw0\n" + "lzz/zlVNuO2U09KIDwDPVG1mBzQiMiSWj1U1pM4KxINkWQwDy/fvu/I983s8lW5z\n" + "hf2WuFNzQN3fcMK5dpBE9NVIu27oYuGYh2sak34v+7T700W2ooBB71qFXtm9P5rl\n" + "Yp9RCEsg3KEEPNTtCBs8fROeXvLDrP0cmBIqwGYDuRNCxFDTOdjv6YGdA8nLOjaH\n" + "2dDk0g==\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * Below is the private key for all the server certificates above (but * not the intermediate CA or the root CA). All of those server certs * came from the same private key. */ public final static String RSA_PUBLIC_MODULUS = "00c863af96823e8ca9d11d62ae85807e713204c1985a80a2747f7ac863c5" + "8d82e8c1ecf9698298d4838a4d8d81958868e0ef385f6e3842b653465f24" + "41b62dc671a1e204820fe67c82367f80cbcb52586a39bf965cf0141cc077" + "f46472cdeac0fd9b6b954a9ffa52a8d2e59ca1cc5e45cefbd4a37c70f1f7" + "9c7674ad5d07c78640672e94e31c4e6dee2bb52558d3b84d29701bda8767" + "56a83371888390b57c8a5bc49a8356316ae9f1406a913729121621098a77" + "713920270312baabfc06a9c677cef1414dc5559238b5bb6407e2b38c3f73" + "cfc4020c901f0e3647474dca350e66c4e817c31c0ac3a94631a895253c69" + "4caab29bddf085893dde5de87047b9e5cd"; public final static String RSA_PUBLIC_EXPONENT = "65537"; public final static String RSA_PRIVATE_EXPONENT = "577abd3295553d0efd4d38c13b62a6d03fa7b7e40cce4f1d5071877d96c6" + "7a39a63f0f7ab21a89db8acae45587b3ef251309a70f74dc1ac02bde68f3" + "8ed658e54e685ed370a18c054449512ea66a2252ed36e82b565b5159ec83" + "f23df40ae189550a183865b25fd77789e960f0d8cedcd72f32d7a66edb4b" + "a0a2baf3fbeb6c7d75f56ef0af9a7cff1c8c7f297d72eae7982164e50a89" + "d450698cf598d39343201094241d2d180a95882a7111e58f4a5bdbc5c125" + "a967dd6ed9ec614c5853e88e4c71e8b682a7cf89cb1d82b6fe78cc865084" + "c8c5dfbb50c939df2b839c977b0245bfa3615e0592b527b1013d5b675ecb" + "44e6b355c1df581f50997175166eef39"; public final static String RSA_PRIME1 = "00fe759c4f0ce8b763880215e82767e7a937297668f4e4b1e119c6b22a3c" + "a2c7b06c547d88d0aa45f645d7d3aeadaf7f8bc594deae0978529592977c" + "b1ff890f05033a9e9e15551cad9fbf9c41d12139ccd99c1c3ac7b2197eff" + "350d236bb900c1440953b64956e0a058ef824a2e16894af175177c77dbe1" + "fef7d8b532608d2513"; public final static String RSA_PRIME2 = "00c99a45878737a4cf73f9896680b75487f1b669b7686a6ba07103856f31" + "db668c2c440c44cdd116f708f631c37a9adf119f5b5cb58ffe3dc62e20af" + "af72693d936dc6bb3c5194996468389c1f094079b81522e94572b4ad7d39" + "529178e9b8ebaeb1f0fdd83b8731c5223f1dea125341d1d64917f6b1a6ae" + "c18d320510d79f859f"; public final static String RSA_EXPONENT1 = "029febf0d4cd41b7011c2465b4a259bd6118486464c247236f44a169d61e" + "47b9062508f674508d5031003ceabc57e714e600d71b2c75d5443db2da52" + "6bb45a374f0537c5a1aab3150764ce93cf386c84346a6bd01f6732e42075" + "c7a0e9e78a9e73b934e7d871d0f75673820089e129a1604438edcbbeb4e2" + "106467da112ce389"; public final static String RSA_EXPONENT2 = "00827e76650c946afcd170038d32e1f8386ab00d6be78d830efe382e45d4" + "7ad4bd04e6231ee22e66740efbf52838134932c9f8c460cdccdec58a1424" + "4427859192fd6ab6c58b74e97941b0eaf577f2a11713af5e5952af3ae124" + "9a9a892e98410dfa2628d9af668a43b5302fb7d496c9b2fec69f595292b6" + "e997f079b0f6314eb7"; public final static String RSA_COEFFICIENT = "00e6b62add350f1a2a8968903ff76c31cf703b0d7326c4a620aef01225b7" + "1640b3f2ec375208c5f7299863f6005b7799b6e529bb1133c8435bf5fdb5" + "a786f6cd8a19ee7094a384e6557c600a38845a0960ddbfd1df18d0af5740" + "001853788f1b5ccbf9affb4c52c9d2efdb8aab0183d86735b32737fb4e79" + "2b8a9c7d91c7d175ae"; /** * subjectAlt=IP Address:127.0.0.1, email:oleg@ural.ru, DNS:localhost.localdomain */ public final static byte[] X509_MULTIPLE_SUBJECT_ALT = ( "-----BEGIN CERTIFICATE-----\n" + "MIIDcTCCAtqgAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJDSDEL\n" + "MAkGA1UECBMCWkgxDzANBgNVBAcTBlp1cmljaDETMBEGA1UEAxMKTXkgVGVzdCBD\n" + "QTAeFw0wODEwMzExMTU3NDVaFw0wOTEwMzExMTU3NDVaMGkxCzAJBgNVBAYTAkNI\n" + "MRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdV\n" + "bmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwggG4\n" + "MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/\n" + "gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQ\n" + "IsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZ\n" + "ndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5\n" + "eZSvu/o66oL5V0wLPQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbh\n" + "PBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7YnoBJDvMpPG+qFGQiaiD3+Fa5Z8G\n" + "kotmXoB7VSVkAUw7/s9JKgOBhQACgYEA6ogAb/YLM1Rz9AoXKW4LA70VtFf7Mqqp\n" + "divdu9f72WQc1vMKo1YMf3dQadkMfBYRvAAa1IXDnoiFCHhXnVRkWkoUBJyNebLB\n" + "N92CZc0RVFZiMFgQMEh8UldnvAIi4cBk0/YuN3BGl4MzmquVIGrFovdWGqeaveOu\n" + "Xcu4lKGJNiqjODA2MDQGA1UdEQQtMCuHBH8AAAGBDG9sZWdAdXJhbC5ydYIVbG9j\n" + "YWxob3N0LmxvY2FsZG9tYWluMA0GCSqGSIb3DQEBBQUAA4GBAIgEwIoCSRkU3O7K\n" + "USYaOYyfJB9hsvs6YpClvYXiQ/5kPGARP60pM62v4wC7wI9shEizokIAxY2+O3cC\n" + "vwuJhNYaa2FJMELIwRN3XES8X8R6JHWbPaRjaAAPhczuEd8SZYy8yiVLmJTgw0gH\n" + "BSW775NHlkjsscFVgXkNf0PobqJ9\n" + "-----END CERTIFICATE-----").getBytes(); /** * subject CN=repository.infonotary.com (Multiple AVA in RDN). */ public final static byte[] X509_MULTIPLE_VALUE_AVA = ( "-----BEGIN CERTIFICATE-----\n" + "MIIFxzCCBK+gAwIBAgIIRO/2+/XA7z4wDQYJKoZIhvcNAQEFBQAwgZwxgZkwCQYD\n" + "VQQGDAJCRzAVBgNVBAoMDkluZm9Ob3RhcnkgUExDMBcGCgmSJomT8ixkARkWCWRv\n" + "bWFpbi1jYTAtBgNVBAMMJmktTm90YXJ5IFRydXN0UGF0aCBWYWxpZGF0ZWQgRG9t\n" + "YWluIENBMC0GA1UECwwmaS1Ob3RhcnkgVHJ1c3RQYXRoIFZhbGlkYXRlZCBEb21h\n" + "aW4gQ0EwHhcNMTIwNjE4MDg1MzIyWhcNMTMwNjE4MDg1MzIyWjCBxjGBwzAJBgNV\n" + "BAYTAkJHMBUGA1UEChMOSW5mb05vdGFyeSBQTEMwFwYDVQQLExBGaWxlcyBSZXBv\n" + "c2l0b3J5MBcGCgmSJomT8ixkARkWCWRvbWFpbi1jYTAgBgNVBAMTGXJlcG9zaXRv\n" + "cnkuaW5mb25vdGFyeS5jb20wIwYJKoZIhvcNAQkBFhZzdXBwb3J0QGluZm9ub3Rh\n" + "cnkuY29tMCYGCSqGSIb3DQEJAhMZcmVwb3NpdG9yeS5pbmZvbm90YXJ5LmNvbTCC\n" + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALKWjGpgsuz103xVEW/GSg5I\n" + "tBoLbXPxockabOTHnOh0VO2sImycyhBH78nMj+VMexn4y+kdCOuJqAA5LApxyhTA\n" + "KgKlRN7TfoC90IYHjB1dqLMIseg4YM7Oe0e4Z2nL50bHoqXg7OUHaILUQn7ufpYp\n" + "+VCWxyI43KvaR4+HnST3x47wqeArg/rULGV1a16X+46cxq2eoMAcDfostXHaemvz\n" + "vg/Wd5xcWfPbF/oY1/sBXH+AK+peVBMen82+3GtAWtNWbyPE3bT4RG+WgKUyfLZ1\n" + "7A67rX9DkUEVMPQpa50MpLnrRveiM9w6R3mrMHMHbNnwID0Tqfds5zzOi/7cLD0C\n" + "AwEAAaOCAd8wggHbMA4GA1UdDwEB/wQEAwIDuDATBgNVHSUEDDAKBggrBgEFBQcD\n" + "ATBEBggrBgEFBQcBAQQ4MDYwNAYIKwYBBQUHMAGGKGh0dHA6Ly9vY3NwLmluZm9u\n" + "b3RhcnkuY29tL3Jlc3BvbmRlci5jZ2kwgZAGA1UdIASBiDCBhTCBggYMKwYBBAGB\n" + "rQABAgMBMHIwOAYIKwYBBQUHAgEWLGh0dHA6Ly9yZXBvc2l0b3J5LmluZm9ub3Rh\n" + "cnkuY29tL2RvbWFpbi5odG1sMDYGCCsGAQUFBwICMCoaKGktTm90YXJ5IFZhbGlk\n" + "YXRlZCBEb21haW4gQ2VydGlmaWNhdGUgQ1AwgYkGA1UdHwSBgTB/MDWgL6Athito\n" + "dHRwOi8vY3JsLmluZm9ub3RhcnkuY29tL2NybC9kb21haW4tY2EuY3JsgQIBVjBG\n" + "oECgPoY8bGRhcDovL2xkYXAuaW5mb25vdGFyeS5jb20vZGM9ZG9tYWluLWNhLGRj\n" + "PWluZm9ub3RhcnksZGM9Y29tgQIBVjAPBgNVHRMBAf8EBTADAQEAMB0GA1UdDgQW\n" + "BBTImKJZrgV/8n7mHrA0U5EeGsBvbzAfBgNVHSMEGDAWgBTbkorEK+bPdVPpvyVI\n" + "PTxGFnuOoDANBgkqhkiG9w0BAQUFAAOCAQEAhsMbqsqvkbfVaKZ+wDY9rX3EtuDS\n" + "isdAo4AjmWgTtj/aBGiEiXcIGP312x+0JF+mEEQ75ZOKN+WsM8eLB0F4aqylklk7\n" + "6yRYauRXp8dfbXrT3ozxekt0cpSMqbzze456krI12nL+C00V2Iwq96k5J/yZboNW\n" + "Q+ibCaEAHNiL4tGVHSHm6znkWvIuUTbDgDEsm5RdafO27suz5H6zMnV+VE6onN1J\n" + "I1mQmUs44cg2HZAqnFBpDyJQhNYy8M7yGVaRkbfuVaMqiPa+xDPR5v7NFB3kxRq2\n" + "Za2Snopi52eUxDEhJ0MNqFi3Jfj/ZSmJ+XHra5lU4R8lijCAq8SVLZCmIQ==\n" + "-----END CERTIFICATE-----").getBytes(); public final static byte[] S_GOOGLE_COM = ( "-----BEGIN CERTIFICATE-----\n" + "MIICpzCCAY+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDDAwqLmdv\n" + "b2dsZS5jb20wHhcNMTcwMTEzMjI0OTAzWhcNMTgwMTEzMjI0OTAzWjAXMRUwEwYD\n" + "VQQDDAwqLmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\n" + "AQDHuzznuHdJ5PH344xCyGYnUnIRhyLGBKN3WDLLrXWtr/5Sf3Q1qkiMiJ4BINsh\n" + "3Xy0z7VvHmMFlntgHXtkofBUPvTihxsVIypRkCZb5hpsWLotR10AW2JpVl/oxLP2\n" + "227/36X1zKh33fjImLJl9KzGWHLsbCBleQQJOn7YRsNR/QBZO0XGGkN/R2rRfLF3\n" + "rseRfI5gJjZkO0WDxocnf/iieOe0XNR0NAZaY1aozzPmZ/pRrOKYB8OFH7F73WOC\n" + "lPIUGai/byJ9SpbXdLUcMlGhml/4XzcnV/WVRD2P/mlY+xEFG3UEy3ufhNnKFJul\n" + "yjZrOaKbagamqtOyktzkjnerAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBADaMcwVs\n" + "w5kbnoDJzMBJ01H16T4u8k78i/ybwz7u7krgkU0tABXCRj7S/4Dt3jqQ/rV6evj4\n" + "gIJ/2kZUp/PHKkV7CxWI48XBTAQUu9LEpxj0Hut3AtNMD9y/J6cFn2978tWsHFHI\n" + "mYgvclKUDE4WFMvuxfQVuX3RcGQ5i8khEMczY/KVhZYDcLU1PU0GTTJqqrQm59Z4\n" + "T4UyI3OPBR7Nb/kaU1fcgQ083uxRXcNYRMMZnU6c2oFnR+c6pO6aGoXo0C6rgC4R\n" + "pOj4hPvHCfZO2xg6HAdQ7UPALLX8pu5KGot7GRc8yiJ/Q1nBEuiPKKu0MIwQoFgP\n" + "WUux/APTsgLR7Vc=\n" + "-----END CERTIFICATE-----" ).getBytes(); public final static byte[] IP_1_1_1_1 = ( "-----BEGIN CERTIFICATE-----\n" + "MIICwjCCAaqgAwIBAgIBATANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDEw9kdW1t\n" + "eS12YWx1ZS5jb20wHhcNMTcwMTEzMjI1MTQ2WhcNMTgwMTEzMjI1MTQ2WjAaMRgw\n" + "FgYDVQQDEw9kdW1teS12YWx1ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\n" + "ggEKAoIBAQDfrapp3jHLp1RlElzpR/4sF9AcTYwMF1N+adkHRoVtmTlJV2lTIAjn\n" + "QLauy0Kkzv8uxmbID3uROgrFNDQ5RxTTCe+kW/vE6Pyzr5Z5ayjSTKeycTE7mAC4\n" + "6ntoCeEWiD593zlfqVo5PuRSp9Kusd+kexNVjC/BETDPa3yXctcH1ouW9GyGItgQ\n" + "u4GhCE8cipKMuTltgfK+Gh/5e9lFG9/F2fD+wHUVBULLR3JOQoqwgk2zAwKDwLuS\n" + "sEd1CBi35+W3apCKN0SEdTKIAxc/R+O/1j2hpOl9yXCCYyveGwJdFXVZtDcx+9/H\n" + "7NXhOdmw/mTXC5fOQGKciEo2SXt8Wp89AgMBAAGjEzARMA8GA1UdEQQIMAaHBAEB\n" + "AQEwDQYJKoZIhvcNAQEFBQADggEBAEAO6CE8twpcfdjk9oMjI5nX9GdC5Wt6+ujd\n" + "tLj0SbXvMKzCLLkveT0xTEzXfyEo8KW2qYYvPP1h83BIxsbR/J3Swt35UQVofv+4\n" + "JgO0FIdgB+iLEcjUh5+60xslylqWE+9bSWm4f06OXuv78tq5NYPZKku/3i4tqLRp\n" + "gH2rTtjX7Q4olSS7GdAgfiA2AnDZAbMtxtsnTt/QFpYQqhlkqHVDwgkGP7C8aMBD\n" + "RH0UIQCPxUkhwhtNmVyHO42r6oHXselZoVU6XRHuhogrGxPf/pzDUvrKBiJhsZQQ\n" + "oEu+pZCwkFLiNwUoq1G2oDpkkdBWB0JcBXB2Txa536ezFFWZYc0=\n" + "-----END CERTIFICATE-----" ).getBytes(); public final static byte[] EMAIL_ALT_SUBJECT_NAME = ( "-----BEGIN CERTIFICATE-----\n" + "MIIDpTCCAo2gAwIBAgIJANqkMEtlkelbMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV\n" + "BAYTAlVTMQswCQYDVQQIDAJWQTERMA8GA1UEBwwIU29tZUNpdHkxEjAQBgNVBAoM\n" + "CU15Q29tcGFueTETMBEGA1UECwwKTXlEaXZpc2lvbjEYMBYGA1UEAwwPd3d3LmNv\n" + "bXBhbnkuY29tMB4XDTE4MDIxNTA3MjkzMFoXDTIwMDIxNTA3MjkzMFowcDELMAkG\n" + "A1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0eTESMBAGA1UE\n" + "CgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRgwFgYDVQQDDA93d3cu\n" + "Y29tcGFueS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4v6Oq\n" + "Ua0goRVn1cmT7MOpJhXFm3A70bTpvJIRpEjtGIz99hb34/9r5AYyf1VhKyWmBq24\n" + "XNcOJ59XOlyjjbm2Tl811ufTOdcNbPadoVBmMt4039OSUFpVb4wAw2XPWLTCG2h1\n" + "HNj9GuFHmwcDsg5EiIRrhDGQm2LLLAGoe5PdReoMZCeeWzNWvKTCV14pyRzwQhJL\n" + "F1OmzLYzovbPfB8LZVhQgDbLsh034FScivf2oKDB+NEzAEagNpnrFR0MFLWGYsu1\n" + "nWD5RiZi78HFGiibmhH7QrEPfGlo2eofuUga6naoBUROqkmMCIL8n1HZ/Ur0oGny\n" + "vQCj1AyrfOhuVC53AgMBAAGjQjBAMAsGA1UdDwQEAwIEMDATBgNVHSUEDDAKBggr\n" + "BgEFBQcDATAcBgNVHREEFTATgRFlbWFpbEBleGFtcGxlLmNvbTANBgkqhkiG9w0B\n" + "AQsFAAOCAQEAZ0IsqRrsEmJ6Fa9Yo6PQtrKJrejN2TTDddVgyLQdokzWh/25JFad\n" + "NCMYPH5KjTUyKf96hJDlDayjbKk1PMMhSZMU5OG9NOuGMH/dQttruG1ojse7KIKg\n" + "yHDQrfq5Exxgfa7CMHRKAoTCY7JZhSLyVbTMVhmGfuUDad/RA86ZisXycp0ZmS97\n" + "qDkAmzFL0sL0ZUWNNUh4ZUWvCUZwiuN08z70NjGqXMTDCf68p3SYxbII0xTfScgf\n" + "aQ/A/hD7IbGGTexeoTwpEj01DNvefbQV6//neo32/R5XD0D5jn3TCgZcMThA6H3a\n" + "VkEghVg+s7uMfL/UEebOBQWXQJ/uVoknMA==\n" + "-----END CERTIFICATE-----" ).getBytes(); /** * subject CN=www.foo.com, subjectAlt=IP Address:127.0.0.1 */ public final static byte[] SUBJECT_ALT_IP_ONLY = ( "-----BEGIN CERTIFICATE-----\n" + "MIIBQjCB6qADAgECAgYBeLZWSL0wCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwLd3d3\n" + "LmZvby5jb20wHhcNMjEwNDA5MTExMzI2WhcNMjEwNDA5MTE0MzMxWjAWMRQwEgYD\n" + "VQQDDAt3d3cuZm9vLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPC+8O/v\n" + "IPWSC/iPdPAgpgzpyLKZevNH8ENOb6PaJRDyNHdd1MbJvurKtJ+HP6UYV3keNHUk\n" + "r657s2JjufiTmuSjJDAiMAwGA1UdEwQFMAMBAf8wEgYDVR0RAQH/BAgwBocEfwAA\n" + "ATAKBggqhkjOPQQDAgNHADBEAiA2svKw50Mr5nnF4TXyFcvzhJWkC+7m46JROMiy\n" + "TMt3BQIgK5IHScVH6Cbi106y+BILx4U0Ygt5IFNnMx/K+Jusuls=\n" + "-----END CERTIFICATE-----" ).getBytes(); } TestDefaultHostnameVerifier.java000066400000000000000000000611621434266521000377210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.List; import javax.net.ssl.SSLException; import org.apache.hc.client5.http.psl.DomainType; import org.apache.hc.client5.http.psl.PublicSuffixList; import org.apache.hc.client5.http.psl.PublicSuffixListParser; import org.apache.hc.client5.http.psl.PublicSuffixMatcher; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Unit tests for {@link org.apache.hc.client5.http.ssl.DefaultHostnameVerifier}. */ public class TestDefaultHostnameVerifier { private DefaultHostnameVerifier impl; private PublicSuffixMatcher publicSuffixMatcher; private DefaultHostnameVerifier implWithPublicSuffixCheck; private static final String PUBLIC_SUFFIX_MATCHER_SOURCE_FILE = "suffixlistmatcher.txt"; @BeforeEach public void setup() throws IOException { impl = new DefaultHostnameVerifier(); // Load the test PublicSuffixMatcher final ClassLoader classLoader = getClass().getClassLoader(); final InputStream in = classLoader.getResourceAsStream(PUBLIC_SUFFIX_MATCHER_SOURCE_FILE); Assertions.assertNotNull(in); final List lists = PublicSuffixListParser.INSTANCE.parseByType( new InputStreamReader(in, StandardCharsets.UTF_8)); publicSuffixMatcher = new PublicSuffixMatcher(lists); implWithPublicSuffixCheck = new DefaultHostnameVerifier(publicSuffixMatcher); } @Test public void testVerify() throws Exception { final CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream in; X509Certificate x509; in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); exceptionPlease(impl, "bar.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("\u82b1\u5b50.co.jp", x509); exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); impl.verify("bar.com", x509); exceptionPlease(impl, "a.bar.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); impl.verify("bar.com", x509); exceptionPlease(impl, "a.bar.com", x509); /* Java isn't extracting international subjectAlts properly. (Or OpenSSL isn't storing them properly). */ // DEFAULT.verify("\u82b1\u5b50.co.jp", x509 ); // impl.verify("\u82b1\u5b50.co.jp", x509 ); exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_THREE_CNS_FOO_BAR_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); exceptionPlease(impl, "bar.com", x509); exceptionPlease(impl, "a.bar.com", x509); impl.verify("\u82b1\u5b50.co.jp", x509); exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); impl.verify("www.foo.com", x509); impl.verify("\u82b1\u5b50.foo.com", x509); exceptionPlease(impl, "a.b.foo.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP); x509 = (X509Certificate) cf.generateCertificate(in); // Silly test because no-one would ever be able to lookup an IP address // using "*.co.jp". impl.verify("*.co.jp", x509); impl.verify("foo.co.jp", x509); impl.verify("\u82b1\u5b50.co.jp", x509); exceptionPlease(implWithPublicSuffixCheck, "foo.co.jp", x509); exceptionPlease(implWithPublicSuffixCheck, "\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO_BAR_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); // try the foo.com variations exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "www.foo.com", x509); exceptionPlease(impl, "\u82b1\u5b50.foo.com", x509); exceptionPlease(impl, "a.b.foo.com", x509); // try the bar.com variations exceptionPlease(impl, "bar.com", x509); impl.verify("www.bar.com", x509); impl.verify("\u82b1\u5b50.bar.com", x509); exceptionPlease(impl, "a.b.bar.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("repository.infonotary.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("*.google.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("*.Google.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.IP_1_1_1_1); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("1.1.1.1", x509); impl.verify("dummy-value.com", x509); exceptionPlease(impl, "1.1.1.2", x509); exceptionPlease(impl, "not-the-cn.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("www.company.com", x509); } @Test public void testSubjectAlt() throws Exception { final CertificateFactory cf = CertificateFactory.getInstance("X.509"); final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_SUBJECT_ALT); final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); Assertions.assertEquals("CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=CH", x509.getSubjectDN().getName()); impl.verify("localhost.localdomain", x509); impl.verify("127.0.0.1", x509); Assertions.assertThrows(SSLException.class, () -> impl.verify("localhost", x509)); Assertions.assertThrows(SSLException.class, () -> impl.verify("local.host", x509)); Assertions.assertThrows(SSLException.class, () -> impl.verify("127.0.0.2", x509)); } public void exceptionPlease(final DefaultHostnameVerifier hv, final String host, final X509Certificate x509) { Assertions.assertThrows(SSLException.class, () -> hv.verify(host, x509)); } @Test public void testDomainRootMatching() { Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", null)); Assertions.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "a.b.c")); Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("aa.b.c", "a.b.c")); Assertions.assertFalse(DefaultHostnameVerifier.matchDomainRoot("a.b.c", "aa.b.c")); Assertions.assertTrue(DefaultHostnameVerifier.matchDomainRoot("a.a.b.c", "a.b.c")); } @Test public void testIdentityMatching() { Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.c")); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.c")); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.b.c", "*.b.c")); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.b.c", "*.b.c")); // subdomain not OK Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "*.gov.uk", publicSuffixMatcher)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "*.gov.uk", publicSuffixMatcher)); // Bad 2TLD Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.a.gov.uk", publicSuffixMatcher)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "*.gov.uk", publicSuffixMatcher)); // BBad 2TLD/no subdomain allowed Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.gov.com", "*.gov.com", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.gov.com", "*.gov.com", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.com", "*.gov.com", publicSuffixMatcher)); // no subdomain allowed Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("s.a.gov.uk", "a*.gov.uk", publicSuffixMatcher)); // Bad 2TLD/no subdomain allowed Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.b.*")); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.b.*")); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("a.b.c", "*.*.c")); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "*.*.c")); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.xxx.uk", "a.b.xxx.uk", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.xxx.uk", "*.b.xxx.uk", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("b.xxx.uk", "b.xxx.uk", publicSuffixMatcher)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("b.xxx.uk", "*.xxx.uk", publicSuffixMatcher)); } @Test public void testHTTPCLIENT_1097() { Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.b.c", "a*.b.c")); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("a.b.c", "a*.b.c")); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("a.a.b.c", "a*.b.c")); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict("a.a.b.c", "a*.b.c")); } @Test public void testHTTPCLIENT_1255() { Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity("mail.a.b.c.com", "m*.a.b.c.com")); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict("mail.a.b.c.com", "m*.a.b.c.com")); } @Test public void testHTTPCLIENT_1997_ANY() { // Only True on all domains String domain; // Unknown domain = "dev.b.cloud.a"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); // ICANN domain = "dev.b.cloud.com"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); // PRIVATE domain = "dev.b.cloud.lan"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher)); } @Test public void testHTTPCLIENT_1997_ICANN() { // Only True on ICANN domains String domain; // Unknown domain = "dev.b.cloud.a"; Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN)); // ICANN domain = "dev.b.cloud.com"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN)); // PRIVATE domain = "dev.b.cloud.lan"; Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.ICANN)); } @Test public void testHTTPCLIENT_1997_PRIVATE() { // Only True on PRIVATE domains String domain; // Unknown domain = "dev.b.cloud.a"; Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE)); // ICANN domain = "dev.b.cloud.com"; Assertions.assertFalse(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE)); Assertions.assertFalse(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE)); // PRIVATE domain = "dev.b.cloud.lan"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.PRIVATE)); } @Test public void testHTTPCLIENT_1997_UNKNOWN() { // Only True on all domains (same as ANY) String domain; // Unknown domain = "dev.b.cloud.a"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN)); // ICANN domain = "dev.b.cloud.com"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN)); // PRIVATE domain = "dev.b.cloud.lan"; Assertions.assertTrue(DefaultHostnameVerifier.matchIdentity( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN)); Assertions.assertTrue(DefaultHostnameVerifier.matchIdentityStrict( "service.apps." + domain, "*.apps." + domain, publicSuffixMatcher, DomainType.UNKNOWN)); } @Test // Check compressed IPv6 hostname matching public void testHTTPCLIENT_1316() throws Exception{ final String host1 = "2001:0db8:aaaa:bbbb:cccc:0:0:0001"; DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001"))); DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1"))); Assertions.assertThrows(SSLException.class, () -> DefaultHostnameVerifier.matchIPv6Address(host1, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10")))); final String host2 = "2001:0db8:aaaa:bbbb:cccc::1"; DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc:0:0:0001"))); DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::1"))); Assertions.assertThrows(SSLException.class, () -> DefaultHostnameVerifier.matchIPv6Address(host2, Collections.singletonList(SubjectName.IP("2001:0db8:aaaa:bbbb:cccc::10")))); } @Test public void testHTTPCLIENT_2149() throws Exception { final CertificateFactory cf = CertificateFactory.getInstance("X.509"); final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.SUBJECT_ALT_IP_ONLY); final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in); Assertions.assertEquals("CN=www.foo.com", x509.getSubjectDN().getName()); impl.verify("127.0.0.1", x509); impl.verify("www.foo.com", x509); exceptionPlease(impl, "127.0.0.2", x509); exceptionPlease(impl, "www.bar.com", x509); } @Test public void testExtractCN() throws Exception { Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah")); Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, cn=yada, cn=booh")); Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = pampa , cn = blah , ou = blah , o = blah")); Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=\"blah\", ou=blah, o=blah")); Assertions.assertEquals("blah blah", DefaultHostnameVerifier.extractCN("cn=\"blah blah\", ou=blah, o=blah")); Assertions.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=\"blah, blah\", ou=blah, o=blah")); Assertions.assertEquals("blah, blah", DefaultHostnameVerifier.extractCN("cn=blah\\, blah, ou=blah, o=blah")); Assertions.assertEquals("blah", DefaultHostnameVerifier.extractCN("c = cn=uuh, cn=blah, ou=blah, o=blah")); Assertions.assertThrows(SSLException.class, () -> DefaultHostnameVerifier.extractCN("blah,blah")); Assertions.assertThrows(SSLException.class, () -> DefaultHostnameVerifier.extractCN("cn,o=blah")); } @Test public void testMatchDNSName() throws Exception { DefaultHostnameVerifier.matchDNSName( "host.domain.com", Collections.singletonList(SubjectName.DNS("*.domain.com")), publicSuffixMatcher); DefaultHostnameVerifier.matchDNSName( "host.xx", Collections.singletonList(SubjectName.DNS("*.xx")), publicSuffixMatcher); DefaultHostnameVerifier.matchDNSName( "host.appspot.com", Collections.singletonList(SubjectName.DNS("*.appspot.com")), publicSuffixMatcher); DefaultHostnameVerifier.matchDNSName( "demo-s3-bucket.s3.eu-central-1.amazonaws.com", Collections.singletonList(SubjectName.DNS("*.s3.eu-central-1.amazonaws.com")), publicSuffixMatcher); DefaultHostnameVerifier.matchDNSName( "hostname-workspace-1.local", Collections.singletonList(SubjectName.DNS("hostname-workspace-1.local")), publicSuffixMatcher); Assertions.assertThrows(SSLException.class, () -> DefaultHostnameVerifier.matchDNSName( "host.domain.com", Collections.singletonList(SubjectName.DNS("some.other.com")), publicSuffixMatcher)); DefaultHostnameVerifier.matchDNSName( "host.ec2.compute-1.amazonaws.com", Collections.singletonList(SubjectName.DNS("host.ec2.compute-1.amazonaws.com")), publicSuffixMatcher); DefaultHostnameVerifier.matchDNSName( "host.ec2.compute-1.amazonaws.com", Collections.singletonList(SubjectName.DNS("*.ec2.compute-1.amazonaws.com")), publicSuffixMatcher); DefaultHostnameVerifier.matchDNSName( "ec2.compute-1.amazonaws.com", Collections.singletonList(SubjectName.DNS("ec2.compute-1.amazonaws.com")), publicSuffixMatcher); Assertions.assertThrows(SSLException.class, () -> DefaultHostnameVerifier.matchDNSName( "ec2.compute-1.amazonaws.com", Collections.singletonList(SubjectName.DNS("*.compute-1.amazonaws.com")), publicSuffixMatcher)); } }TestDistinguishedNameParser.java000066400000000000000000000130731434266521000377210ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import static org.hamcrest.MatcherAssert.assertThat; import java.util.Arrays; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Unit tests for {@link DistinguishedNameParser}. */ public class TestDistinguishedNameParser { private DistinguishedNameParser impl; @BeforeEach public void setup() { impl = new DistinguishedNameParser(); } @Test public void testParseBasic() throws Exception { assertThat(impl.parse("cn=blah, ou=yada, o=booh"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("cn", "blah"), new BasicNameValuePair("ou", "yada"), new BasicNameValuePair("o", "booh")))); } @Test public void testParseRepeatedElements() throws Exception { assertThat(impl.parse("cn=blah, cn=yada, cn=booh"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("cn", "blah"), new BasicNameValuePair("cn", "yada"), new BasicNameValuePair("cn", "booh")))); } @Test public void testParseBlanks() throws Exception { assertThat(impl.parse("c = pampa , cn = blah , ou = blah , o = blah"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("c", "pampa"), new BasicNameValuePair("cn", "blah"), new BasicNameValuePair("ou", "blah"), new BasicNameValuePair("o", "blah")))); } @Test public void testParseQuotes() throws Exception { assertThat(impl.parse("cn=\"blah\", ou=yada, o=booh"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("cn", "blah"), new BasicNameValuePair("ou", "yada"), new BasicNameValuePair("o", "booh")))); } @Test public void testParseQuotes2() throws Exception { assertThat(impl.parse("cn=\"blah blah\", ou=yada, o=booh"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("cn", "blah blah"), new BasicNameValuePair("ou", "yada"), new BasicNameValuePair("o", "booh")))); } @Test public void testParseQuotes3() throws Exception { assertThat(impl.parse("cn=\"blah, blah\", ou=yada, o=booh"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("cn", "blah, blah"), new BasicNameValuePair("ou", "yada"), new BasicNameValuePair("o", "booh")))); } @Test public void testParseEscape() throws Exception { assertThat(impl.parse("cn=blah\\, blah, ou=yada, o=booh"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("cn", "blah, blah"), new BasicNameValuePair("ou", "yada"), new BasicNameValuePair("o", "booh")))); } @Test public void testParseUnescapedEqual() throws Exception { assertThat(impl.parse("c = cn=uuh, cn=blah, ou=yada, o=booh"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("c", "cn=uuh"), new BasicNameValuePair("cn", "blah"), new BasicNameValuePair("ou", "yada"), new BasicNameValuePair("o", "booh")))); } @Test public void testParseInvalid() throws Exception { assertThat(impl.parse("blah,blah"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("blah", null), new BasicNameValuePair("blah", null)))); } @Test public void testParseInvalid2() throws Exception { assertThat(impl.parse("cn,o=blah"), CoreMatchers.equalTo(Arrays.asList( new BasicNameValuePair("cn", null), new BasicNameValuePair("o", "blah")))); } } TestSSLSocketFactory.java000066400000000000000000000057531434266521000363100ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.ssl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link SSLConnectionSocketFactory}. */ public class TestSSLSocketFactory { @Test public void testStrongCipherSuites() { final String[] strongCipherSuites = { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_GCM_SHA384" }; for (final String cipherSuite : strongCipherSuites) { Assertions.assertFalse(SSLConnectionSocketFactory.isWeakCipherSuite(cipherSuite)); } } @Test public void testWeakCiphersDisabledByDefault() { final String[] weakCiphersSuites = { "SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_NULL_SHA256", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5" }; for (final String cipherSuite : weakCiphersSuites) { Assertions.assertTrue(SSLConnectionSocketFactory.isWeakCipherSuite(cipherSuite)); } } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/utils/000077500000000000000000000000001434266521000320475ustar00rootroot00000000000000TestBase64.java000066400000000000000000000112131434266521000345150ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import org.junit.jupiter.api.Test; import static java.nio.charset.StandardCharsets.US_ASCII; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class TestBase64 { public static final char CHAR_ZERO = 0; public static final String EMPTY_STR = ""; public static final byte[] EMPTY_BYTES = new byte[0]; public static final String NULL_STR = null; public static final byte[] NULL_BYTE_ARRAY = null; public static final String EMOJI = "\uD83D\uDE15"; public static final char SPACE = ' '; private final Base64 target = new Base64(); @Test void nullHandling() { assertNull(target.decode(NULL_STR)); assertNull(target.decode(NULL_BYTE_ARRAY)); assertNull(Base64.decodeBase64(NULL_STR)); assertNull(Base64.decodeBase64(NULL_BYTE_ARRAY)); assertNull(target.encode(NULL_BYTE_ARRAY)); assertNull(Base64.encodeBase64(NULL_BYTE_ARRAY)); assertNull(Base64.encodeBase64String(NULL_BYTE_ARRAY)); } @Test void zeroLength() { assertArrayEquals(EMPTY_BYTES, target.decode(EMPTY_STR)); assertArrayEquals(EMPTY_BYTES, target.decode(EMPTY_BYTES)); assertArrayEquals(EMPTY_BYTES, Base64.decodeBase64(EMPTY_STR)); assertArrayEquals(EMPTY_BYTES, Base64.decodeBase64(EMPTY_BYTES)); assertArrayEquals(EMPTY_BYTES, target.encode(EMPTY_BYTES)); assertArrayEquals(EMPTY_BYTES, Base64.encodeBase64(EMPTY_BYTES)); assertEquals(EMPTY_STR, Base64.encodeBase64String(EMPTY_BYTES)); } @Test void validValues() { final byte[] unencodedBytes = "Hello World!".getBytes(US_ASCII); checkDecode(unencodedBytes, "SGVsbG8gV29ybGQh"); checkEncode("SGVsbG8gV29ybGQh", unencodedBytes); } @Test void decodeIgnoresEmbeddedInvalidChars() { checkEquivalentDecode(fourOf("A"), " A A A A "); checkEquivalentDecode(fourOf("A"), "AA" + EMOJI + "AA"); } @Test void decodeInvalid() { checkDecode(EMPTY_BYTES, fourOf(EMOJI)); checkDecode(EMPTY_BYTES, "A"); checkDecode(EMPTY_BYTES, "A==="); checkDecode(EMPTY_BYTES, fourOf(SPACE)); checkDecode(EMPTY_BYTES, fourOf('=')); checkDecode(EMPTY_BYTES, fourOf('@')); checkDecode(EMPTY_BYTES, fourOf(CHAR_ZERO)); } @Test void decodeUnpadded() { checkEquivalentDecode("AA==", "AA"); } private void checkDecode(final byte[] expectedDecoded, final String testInput) { final byte[] decoded = target.decode(testInput); assertArrayEquals(expectedDecoded, decoded); } private void checkEncode(final String expectedEncoded, final byte[] testInput) { final byte[] encoded = target.encode(testInput); assertEquals(expectedEncoded, new String(encoded, US_ASCII)); } private void checkEquivalentDecode(final String expectedEquivalentTo, final String testInput) { final byte[] decoded = target.decode(testInput); final byte[] expectedDecoded = java.util.Base64.getDecoder().decode(expectedEquivalentTo); assertArrayEquals(expectedDecoded, decoded); } private static String fourOf(final char c) { final String charStr = String.valueOf(c); return fourOf(charStr); } private static String fourOf(final String str) { return str + str + str + str; } }TestByteArrayBuilder.java000066400000000000000000000147411434266521000367130ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * {@link ByteArrayBuilder} test cases. */ public class TestByteArrayBuilder { @Test public void testEmptyBuffer() throws Exception { final ByteArrayBuilder buffer = new ByteArrayBuilder(); final ByteBuffer byteBuffer = buffer.toByteBuffer(); Assertions.assertNotNull(byteBuffer); Assertions.assertEquals(0, byteBuffer.capacity()); final byte[] bytes = buffer.toByteArray(); Assertions.assertNotNull(bytes); Assertions.assertEquals(0, bytes.length); } @Test public void testAppendBytes() throws Exception { final ByteArrayBuilder buffer = new ByteArrayBuilder(); buffer.append(new byte[]{1, 2, 3, 4, 5}); buffer.append(new byte[]{3, 4, 5, 6, 7, 8, 9, 10, 11}, 3, 5); buffer.append((byte[]) null); final byte[] bytes = buffer.toByteArray(); Assertions.assertNotNull(bytes); Assertions.assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, bytes); } @Test public void testInvalidAppendBytes() throws Exception { final ByteArrayBuilder buffer = new ByteArrayBuilder(); buffer.append((byte[])null, 0, 0); final byte[] tmp = new byte[] { 1, 2, 3, 4}; Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4)); } @Test public void testEnsureCapacity() throws Exception { final ByteArrayBuilder buffer = new ByteArrayBuilder(); buffer.ensureFreeCapacity(10); Assertions.assertEquals(10, buffer.capacity()); buffer.ensureFreeCapacity(5); Assertions.assertEquals(10, buffer.capacity()); buffer.append(new byte[]{1, 2, 3, 4, 5, 6, 7, 8}); buffer.ensureFreeCapacity(5); Assertions.assertEquals(13, buffer.capacity()); buffer.ensureFreeCapacity(15); Assertions.assertEquals(23, buffer.capacity()); } @Test public void testAppendText() throws Exception { final ByteArrayBuilder buffer = new ByteArrayBuilder(); buffer.append(new char[]{'1', '2', '3', '4', '5'}); buffer.append(new char[]{'3', '4', '5', '6', '7', '8', '9', 'a', 'b'}, 3, 5); buffer.append("bcd"); buffer.append("e"); buffer.append("f"); buffer.append(""); buffer.append((String) null); buffer.append((char[]) null); final byte[] bytes = buffer.toByteArray(); Assertions.assertNotNull(bytes); Assertions.assertEquals("123456789abcdef", new String(bytes, StandardCharsets.US_ASCII)); } @Test public void testInvalidAppendChars() throws Exception { final ByteArrayBuilder buffer = new ByteArrayBuilder(); buffer.append((char[])null, 0, 0); final char[] tmp = new char[] { 1, 2, 3, 4}; Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, -1, 0)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, -1)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 0, 8)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 10, Integer.MAX_VALUE)); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> buffer.append(tmp, 2, 4)); } @Test public void testReset() throws Exception { final ByteArrayBuilder buffer = new ByteArrayBuilder(); buffer.append("abcd"); buffer.append("e"); buffer.append("f"); final byte[] bytes1 = buffer.toByteArray(); Assertions.assertNotNull(bytes1); Assertions.assertEquals("abcdef", new String(bytes1, StandardCharsets.US_ASCII)); buffer.reset(); final byte[] bytes2 = buffer.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals("", new String(bytes2, StandardCharsets.US_ASCII)); } @Test public void testNonAsciiCharset() throws Exception { final int[] germanChars = { 0xE4, 0x2D, 0xF6, 0x2D, 0xFc }; final StringBuilder tmp = new StringBuilder(); for (final int germanChar : germanChars) { tmp.append((char) germanChar); } final String umlauts = tmp.toString(); final ByteArrayBuilder buffer = new ByteArrayBuilder(); buffer.append(umlauts); final byte[] bytes1 = buffer.toByteArray(); Assertions.assertNotNull(bytes1); Assertions.assertEquals("?-?-?", new String(bytes1, StandardCharsets.US_ASCII)); buffer.reset(); buffer.charset(StandardCharsets.UTF_8); buffer.append(umlauts); final byte[] bytes2 = buffer.toByteArray(); Assertions.assertNotNull(bytes2); Assertions.assertEquals(umlauts, new String(bytes2, StandardCharsets.UTF_8)); } } TestDateUtils.java000066400000000000000000000126331434266521000353760ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import java.time.Instant; import java.time.LocalDate; import java.time.Month; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Date; import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.HeaderGroup; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * Unit tests for {@link DateUtils}. */ public class TestDateUtils { private static Instant createInstant(final int year, final Month month, final int day) { return LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant(); } private static Date createDate(final int year, final Month month, final int day) { final Instant instant = createInstant(year, month, day); return new Date(instant.toEpochMilli()); } @Test public void testBasicDateParse() throws Exception { final Instant instant = createInstant(2005, Month.OCTOBER, 14); Assertions.assertEquals(instant, DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1123)); Assertions.assertEquals(instant, DateUtils.parseDate("Friday, 14 Oct 2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1123)); Assertions.assertEquals(instant, DateUtils.parseDate("Fri, 14-Oct-2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1036)); Assertions.assertEquals(instant, DateUtils.parseDate("Friday, 14-Oct-2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1036)); Assertions.assertEquals(instant.minus(2, ChronoUnit.HOURS), DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 CET", DateUtils.FORMATTER_RFC1123)); Assertions.assertEquals(instant.minus(2, ChronoUnit.HOURS), DateUtils.parseDate("Fri, 14-Oct-05 00:00:00 CET", DateUtils.FORMATTER_RFC1036)); Assertions.assertEquals(instant, DateUtils.parseStandardDate("Fri, 14 Oct 2005 00:00:00 GMT")); } @Test public void testDateParseMessage() throws Exception { final HeaderGroup message1 = new HeaderGroup(); message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Fri, 14 Oct 2005 00:00:00 GMT")); Assertions.assertEquals(createInstant(2005, Month.OCTOBER, 14), DateUtils.parseStandardDate(message1, HttpHeaders.DATE)); final HeaderGroup message2 = new HeaderGroup(); message2.addHeader(new BasicHeader(HttpHeaders.DATE, "Fri, 14 Oct 2005 00:00:00 GMT")); message2.addHeader(new BasicHeader(HttpHeaders.DATE, "Fri, 21 Oct 2005 00:00:00 GMT")); Assertions.assertEquals(createInstant(2005, Month.OCTOBER, 14), DateUtils.parseStandardDate(message2, HttpHeaders.DATE)); } @Test public void testMalformedDate() { Assertions.assertNull(DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", new DateTimeFormatter[] {})); } @Test public void testInvalidInput() throws Exception { Assertions.assertThrows(NullPointerException.class, () -> DateUtils.parseStandardDate(null)); Assertions.assertThrows(NullPointerException.class, () -> DateUtils.formatStandardDate(null)); } @Test public void testTwoDigitYearDateParse() throws Exception { Assertions.assertEquals(createInstant(2005, Month.OCTOBER, 14), DateUtils.parseDate("Friday, 14-Oct-05 00:00:00 GMT", DateUtils.FORMATTER_RFC1036)); } @Test public void testParseQuotedDate() throws Exception { Assertions.assertEquals(createInstant(2005, Month.OCTOBER, 14), DateUtils.parseDate("'Fri, 14 Oct 2005 00:00:00 GMT'", DateUtils.FORMATTER_RFC1123)); } @Test public void testBasicDateFormat() throws Exception { final Instant instant = createInstant(2005, Month.OCTOBER, 14); Assertions.assertEquals("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.formatStandardDate(instant)); Assertions.assertEquals("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.formatDate(instant, DateUtils.FORMATTER_RFC1123)); Assertions.assertEquals("Fri, 14-Oct-05 00:00:00 GMT", DateUtils.formatDate(instant, DateUtils.FORMATTER_RFC1036)); Assertions.assertEquals("Fri Oct 14 00:00:00 2005", DateUtils.formatDate(instant, DateUtils.FORMATTER_ASCTIME)); } } TestDnsUtils.java000066400000000000000000000040621434266521000352420ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import static org.hamcrest.MatcherAssert.assertThat; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; /** * Unit tests for DnsUtils. */ public class TestDnsUtils { @Test public void testNormalize() { assertThat(DnsUtils.normalize(null), CoreMatchers.equalTo(null)); assertThat(DnsUtils.normalize(""), CoreMatchers.equalTo("")); assertThat(DnsUtils.normalize("blah"), CoreMatchers.equalTo("blah")); assertThat(DnsUtils.normalize("BLAH"), CoreMatchers.equalTo("blah")); assertThat(DnsUtils.normalize("blAh"), CoreMatchers.equalTo("blah")); assertThat(DnsUtils.normalize("blaH"), CoreMatchers.equalTo("blah")); assertThat(DnsUtils.normalize("blaH"), CoreMatchers.equalTo("blah")); assertThat(DnsUtils.normalize("hac\u212A!!!"), CoreMatchers.equalTo("hac\u212A!!!")); } } TestURIUtils.java000066400000000000000000000340721434266521000351610ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/java/org/apache/hc/client5/http/utils/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * */ package org.apache.hc.client5.http.utils; import java.net.URI; import java.util.Arrays; import java.util.Collections; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.net.URIBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** * This TestCase contains test methods for URI resolving according to RFC 3986. * The examples are listed in section "5.4 Reference Resolution Examples" */ public class TestURIUtils { private final URI baseURI = URI.create("http://a/b/c/d;p?q"); @Test public void testNormalization() { Assertions.assertEquals("example://a/b/c/%7Bfoo%7D", URIUtils.resolve(this.baseURI, "eXAMPLE://a/./b/../b/%63/%7bfoo%7d").toString()); Assertions.assertEquals("http://www.example.com/%3C", URIUtils.resolve(this.baseURI, "http://www.example.com/%3c").toString()); Assertions.assertEquals("http://www.example.com/", URIUtils.resolve(this.baseURI, "HTTP://www.EXAMPLE.com/").toString()); Assertions.assertEquals("http://www.example.com/a%2F", URIUtils.resolve(this.baseURI, "http://www.example.com/a%2f").toString()); Assertions.assertEquals("http://www.example.com/?q=%26", URIUtils.resolve(this.baseURI, "http://www.example.com/?q=%26").toString()); Assertions.assertEquals("http://www.example.com/%23?q=%26", URIUtils.resolve(this.baseURI, "http://www.example.com/%23?q=%26").toString()); Assertions.assertEquals("http://www.example.com/blah-%28%20-blah-%20%26%20-blah-%20%29-blah/", URIUtils.resolve(this.baseURI, "http://www.example.com/blah-%28%20-blah-%20&%20-blah-%20)-blah/").toString()); } @Test public void testResolve() { Assertions.assertEquals("g:h", URIUtils.resolve(this.baseURI, "g:h").toString()); Assertions.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "g").toString()); Assertions.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "./g").toString()); Assertions.assertEquals("http://a/b/c/g/", URIUtils.resolve(this.baseURI, "g/").toString()); Assertions.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/g").toString()); Assertions.assertEquals("http://g/", URIUtils.resolve(this.baseURI, "//g").toString()); Assertions.assertEquals("http://a/b/c/d;p?y", URIUtils.resolve(this.baseURI, "?y").toString()); Assertions.assertEquals("http://a/b/c/d;p?y#f", URIUtils.resolve(this.baseURI, "?y#f") .toString()); Assertions.assertEquals("http://a/b/c/g?y", URIUtils.resolve(this.baseURI, "g?y").toString()); Assertions.assertEquals("http://a/b/c/d%3Bp?q#s", URIUtils.resolve(this.baseURI, "#s") .toString()); Assertions.assertEquals("http://a/b/c/g#s", URIUtils.resolve(this.baseURI, "g#s").toString()); Assertions.assertEquals("http://a/b/c/g?y#s", URIUtils.resolve(this.baseURI, "g?y#s") .toString()); Assertions.assertEquals("http://a/b/c/%3Bx", URIUtils.resolve(this.baseURI, ";x").toString()); Assertions.assertEquals("http://a/b/c/g%3Bx", URIUtils.resolve(this.baseURI, "g;x").toString()); Assertions.assertEquals("http://a/b/c/g%3Bx?y#s", URIUtils.resolve(this.baseURI, "g;x?y#s") .toString()); Assertions.assertEquals("http://a/b/c/d%3Bp?q", URIUtils.resolve(this.baseURI, "").toString()); Assertions.assertEquals("http://a/b/c/", URIUtils.resolve(this.baseURI, ".").toString()); Assertions.assertEquals("http://a/b/c/", URIUtils.resolve(this.baseURI, "./").toString()); Assertions.assertEquals("http://a/b/", URIUtils.resolve(this.baseURI, "..").toString()); Assertions.assertEquals("http://a/b/", URIUtils.resolve(this.baseURI, "../").toString()); Assertions.assertEquals("http://a/b/g", URIUtils.resolve(this.baseURI, "../g").toString()); Assertions.assertEquals("http://a/", URIUtils.resolve(this.baseURI, "../..").toString()); Assertions.assertEquals("http://a/", URIUtils.resolve(this.baseURI, "../../").toString()); Assertions.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../g").toString()); Assertions.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../../g").toString()); Assertions.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../../../g") .toString()); Assertions.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/./g").toString()); Assertions.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/../g").toString()); Assertions.assertEquals("http://a/b/c/g.", URIUtils.resolve(this.baseURI, "g.").toString()); Assertions.assertEquals("http://a/b/c/.g", URIUtils.resolve(this.baseURI, ".g").toString()); Assertions.assertEquals("http://a/b/c/g..", URIUtils.resolve(this.baseURI, "g..").toString()); Assertions.assertEquals("http://a/b/c/..g", URIUtils.resolve(this.baseURI, "..g").toString()); Assertions.assertEquals("http://a/b/g", URIUtils.resolve(this.baseURI, "./../g").toString()); Assertions.assertEquals("http://a/b/c/g/", URIUtils.resolve(this.baseURI, "./g/.").toString()); Assertions.assertEquals("http://a/b/c/g/h", URIUtils.resolve(this.baseURI, "g/./h").toString()); Assertions.assertEquals("http://a/b/c/h", URIUtils.resolve(this.baseURI, "g/../h").toString()); Assertions.assertEquals("http://a/b/c/g%3Bx%3D1/y", URIUtils.resolve(this.baseURI, "g;x=1/./y") .toString()); Assertions.assertEquals("http://a/b/c/y", URIUtils.resolve(this.baseURI, "g;x=1/../y") .toString()); Assertions.assertEquals("http://a/b/c/g?y%2F.%2Fx", URIUtils.resolve(this.baseURI, "g?y/./x") .toString()); Assertions.assertEquals("http://a/b/c/g?y%2F..%2Fx", URIUtils.resolve(this.baseURI, "g?y/../x") .toString()); Assertions.assertEquals("http://a/b/c/g#s%2F.%2Fx", URIUtils.resolve(this.baseURI, "g#s/./x") .toString()); Assertions.assertEquals("http://a/b/c/g#s%2F..%2Fx", URIUtils.resolve(this.baseURI, "g#s/../x") .toString()); Assertions.assertEquals("http:g", URIUtils.resolve(this.baseURI, "http:g").toString()); // examples from section 5.2.4 Assertions.assertEquals("http://s/a/g", URIUtils.resolve(this.baseURI, "http://s/a/b/c/./../../g").toString()); Assertions.assertEquals("http://s/mid/6", URIUtils.resolve(this.baseURI, "http://s/mid/content=5/../6").toString()); } @Test public void testResolveOpaque() { Assertions.assertEquals("example://a/b/c/%7Bfoo%7D", URIUtils.resolve(this.baseURI, "eXAMPLE://a/./b/../b/%63/%7bfoo%7d").toString()); Assertions.assertEquals("file://localhost/etc/fstab", URIUtils.resolve(this.baseURI, "file://localhost/etc/fstab").toString()); Assertions.assertEquals("file:///etc/fstab", URIUtils.resolve(this.baseURI, "file:///etc/fstab").toString()); Assertions.assertEquals("file://localhost/c%3A/WINDOWS/clock.avi", URIUtils.resolve(this.baseURI, "file://localhost/c:/WINDOWS/clock.avi").toString()); Assertions.assertEquals("file:///c:/WINDOWS/clock.avi", URIUtils.resolve(this.baseURI, "file:///c:/WINDOWS/clock.avi").toString()); Assertions.assertEquals("file://hostname/path/to/the%20file.txt", URIUtils.resolve(this.baseURI, "file://hostname/path/to/the%20file.txt").toString()); Assertions.assertEquals("file:///c:/path/to/the%20file.txt", URIUtils.resolve(this.baseURI, "file:///c:/path/to/the%20file.txt").toString()); Assertions.assertEquals("urn:issn:1535-3613", URIUtils.resolve(this.baseURI, "urn:issn:1535-3613").toString()); Assertions.assertEquals("mailto:user@example.com", URIUtils.resolve(this.baseURI, "mailto:user@example.com").toString()); Assertions.assertEquals("ftp://example.org/resource.txt", URIUtils.resolve(this.baseURI, "ftp://example.org/resource.txt").toString()); } @Test public void testExtractHost() throws Exception { Assertions.assertEquals(new HttpHost("localhost"), URIUtils.extractHost(new URI("http://localhost/abcd"))); Assertions.assertEquals(new HttpHost("localhost"), URIUtils.extractHost(new URI("http://localhost/abcd%3A"))); Assertions.assertEquals(new HttpHost("local_host"), URIUtils.extractHost(new URI("http://local_host/abcd"))); Assertions.assertEquals(new HttpHost("local_host"), URIUtils.extractHost(new URI("http://local_host/abcd%3A"))); Assertions.assertEquals(new HttpHost("localhost",8), URIUtils.extractHost(new URI("http://localhost:8/abcd"))); Assertions.assertEquals(new HttpHost("local_host",8), URIUtils.extractHost(new URI("http://local_host:8/abcd"))); // URI seems to OK with missing port number Assertions.assertEquals(new HttpHost("localhost",-1),URIUtils.extractHost( new URI("http://localhost:/abcd"))); Assertions.assertEquals(new HttpHost("local_host",-1),URIUtils.extractHost( new URI("http://local_host:/abcd"))); Assertions.assertEquals(new HttpHost("localhost",8080), URIUtils.extractHost(new URI("http://user:pass@localhost:8080/abcd"))); Assertions.assertEquals(new HttpHost("local_host",8080), URIUtils.extractHost(new URI("http://user:pass@local_host:8080/abcd"))); Assertions.assertEquals(new HttpHost("localhost",8080),URIUtils.extractHost( new URI("http://@localhost:8080/abcd"))); Assertions.assertEquals(new HttpHost("local_host",8080),URIUtils.extractHost( new URI("http://@local_host:8080/abcd"))); Assertions.assertEquals(new HttpHost("2a00:1450:400c:c01::69",8080), URIUtils.extractHost(new URI("http://[2a00:1450:400c:c01::69]:8080/"))); Assertions.assertEquals(new HttpHost("localhost",8080), URIUtils.extractHost(new URI("http://localhost:8080/;sessionid=stuff/abcd"))); Assertions.assertNull(URIUtils.extractHost(new URI("http://localhost:8080;sessionid=stuff/abcd"))); Assertions.assertNull(URIUtils.extractHost(new URI("http://localhost:;sessionid=stuff/abcd"))); Assertions.assertNull(URIUtils.extractHost(new URI("http://:80/robots.txt"))); Assertions.assertNull(URIUtils.extractHost(new URI("http://some%20domain:80/robots.txt"))); } @Test public void testHttpLocationWithRelativeFragment() throws Exception { final HttpHost target = new HttpHost("http", "localhost", -1); final URI requestURI = new URI("/stuff#blahblah"); final URI location = URIUtils.resolve(requestURI, target, null); final URI expectedURI = new URIBuilder(requestURI) .setHost(target.getHostName()) .setScheme(target.getSchemeName()) .build(); Assertions.assertEquals(expectedURI, location); } @Test public void testHttpLocationWithAbsoluteFragment() throws Exception { final HttpHost target = new HttpHost("http", "localhost", 80); final URI requestURI = new URIBuilder() .setHost(target.getHostName()) .setScheme(target.getSchemeName()) .setPath("/stuff") .setFragment("blahblah") .build(); final URI location = URIUtils.resolve(requestURI, target, null); final URI expectedURI = requestURI; Assertions.assertEquals(expectedURI, location); } @Test public void testHttpLocationRedirect() throws Exception { final HttpHost target = new HttpHost("http", "localhost", -1); final URI requestURI = new URI("/People.htm#tim"); final URI redirect = new URI("http://localhost/people.html"); final URI location = URIUtils.resolve(requestURI, target, Collections.singletonList(redirect)); final URI expectedURI = new URIBuilder() .setHost(target.getHostName()) .setScheme(target.getSchemeName()) .setPath("/people.html") .setFragment("tim") .build(); Assertions.assertEquals(expectedURI, location); } @Test public void testHttpLocationWithRedirectFragment() throws Exception { final HttpHost target = new HttpHost("http", "localhost", -1); final URI requestURI = new URI("/~tim"); final URI redirect1 = new URI("http://localhost/People.htm#tim"); final URI redirect2 = new URI("http://localhost/people.html"); final URI location = URIUtils.resolve(requestURI, target, Arrays.asList(redirect1, redirect2)); final URI expectedURI = new URIBuilder() .setHost(target.getHostName()) .setScheme(target.getSchemeName()) .setPath("/people.html") .setFragment("tim") .build(); Assertions.assertEquals(expectedURI, location); } } httpcomponents-client-rel-v5.2.1/httpclient5/src/test/resources/000077500000000000000000000000001434266521000250545ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/httpclient5/src/test/resources/commons-logging.properties000066400000000000000000000023501434266521000322710ustar00rootroot00000000000000# ==================================================================== # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ==================================================================== # # This software consists of voluntary contributions made by many # individuals on behalf of the Apache Software Foundation. For more # information on the Apache Software Foundation, please see # . # Disable logging for unit tests org.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog httpcomponents-client-rel-v5.2.1/httpclient5/src/test/resources/log4j2-debug.xml.template000066400000000000000000000025671434266521000316070ustar00rootroot00000000000000 httpcomponents-client-rel-v5.2.1/httpclient5/src/test/resources/log4j2.xml000066400000000000000000000022101434266521000266720ustar00rootroot00000000000000 httpcomponents-client-rel-v5.2.1/httpclient5/src/test/resources/suffixlist.txt000066400000000000000000000023271434266521000300210ustar00rootroot00000000000000// ==================================================================== // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // ==================================================================== // // This software consists of voluntary contributions made by many // individuals on behalf of the Apache Software Foundation. For more // information on the Apache Software Foundation, please see // . // xx jp ac.jp *.tokyo.jp !metro.tokyo.jp // unicode no hå.no httpcomponents-client-rel-v5.2.1/httpclient5/src/test/resources/suffixlist2.txt000066400000000000000000000025241434266521000301020ustar00rootroot00000000000000// ==================================================================== // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // ==================================================================== // // This software consists of voluntary contributions made by many // individuals on behalf of the Apache Software Foundation. For more // information on the Apache Software Foundation, please see // . // // ===BEGIN ICANN DOMAINS=== jp ac.jp *.tokyo.jp !metro.tokyo.jp // ===END ICANN DOMAINS=== // ===BEGIN PRIVATE DOMAINS=== googleapis.com googlecode.com // ===END PRIVATE DOMAINS=== httpcomponents-client-rel-v5.2.1/httpclient5/src/test/resources/suffixlistmatcher.txt000066400000000000000000000027751434266521000313740ustar00rootroot00000000000000// ==================================================================== // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // ==================================================================== // // This software consists of voluntary contributions made by many // individuals on behalf of the Apache Software Foundation. For more // information on the Apache Software Foundation, please see // . // // ===BEGIN PRIVATE DOMAINS=== xx lan appspot.com s3.eu-central-1.amazonaws.com *.compute.amazonaws.com *.compute-1.amazonaws.com *.compute.amazonaws.com.cn us-east-1.amazonaws.com *.xxx.uk // ===END PRIVATE DOMAINS=== // ===BEGIN ICANN DOMAINS=== jp ac.jp *.tokyo.jp !metro.tokyo.jp com co.jp gov.uk // unicode no hå.no // ===END ICANN DOMAINS=== httpcomponents-client-rel-v5.2.1/pom.xml000066400000000000000000000407741434266521000203620ustar00rootroot00000000000000 org.apache.httpcomponents httpcomponents-parent 13 4.0.0 org.apache.httpcomponents.client5 httpclient5-parent Apache HttpComponents Client Parent 5.2.1 Apache HttpComponents Client is a library of components for building client side HTTP services https://hc.apache.org/httpcomponents-client-5.0.x/${project.version}/ 1999 pom Jira https://issues.apache.org/jira/browse/HTTPCLIENT scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-client.git scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-client.git https://github.com/apache/httpcomponents-client/tree/${project.scm.tag} 5.2.1 apache.website Apache HttpComponents Website scm:svn:https://svn.apache.org/repos/asf/httpcomponents/site/components/httpcomponents-client-5.2.x/LATEST/ 1.8 1.8 5.2 2.19.0 0.1.2 2.5.2 3.10.8 2.12.3 1.7.36 5.9.1 2.2 4.8.1 5.12.1 1 2.2.21 5.1 javax.net.ssl.SSLEngine,javax.net.ssl.SSLParameters,java.nio.ByteBuffer,java.nio.CharBuffer 0.15.4 org.apache.httpcomponents.core5 httpcore5 ${httpcore.version} org.apache.httpcomponents.core5 httpcore5-h2 ${httpcore.version} org.apache.httpcomponents.core5 httpcore5-testing ${httpcore.version} org.apache.httpcomponents.core5 httpcore5-reactive ${httpcore.version} org.apache.httpcomponents.client5 httpclient5 ${project.version} org.apache.httpcomponents.client5 httpclient5 ${project.version} tests org.apache.httpcomponents.client5 httpclient5-cache ${project.version} org.apache.httpcomponents.client5 httpclient5-fluent ${project.version} org.apache.httpcomponents.client5 httpclient5-win ${project.version} org.slf4j slf4j-api ${slf4j.version} org.apache.logging.log4j log4j-slf4j-impl ${log4j.version} org.apache.logging.log4j log4j-core ${log4j.version} org.brotli dec ${brotli.version} org.conscrypt conscrypt-openjdk-uber ${conscrypt.version} org.ehcache.modules ehcache-api ${ehcache.version} net.spy spymemcached ${memcached.version} net.java.dev.jna jna ${jna.version} net.java.dev.jna jna-platform ${jna.version} io.reactivex.rxjava2 rxjava ${rxjava.version} test org.junit junit-bom ${junit.version} pom import org.mockito mockito-core ${mockito.version} test org.hamcrest hamcrest ${hamcrest.version} test httpclient5 httpclient5-fluent httpclient5-cache httpclient5-win httpclient5-testing clean verify maven-jar-plugin ${Automatic-Module-Name} ${project.url} maven-javadoc-plugin https://hc.apache.org/httpcomponents-core-5.0.x/httpcore5/apidocs/ https://hc.apache.org/httpcomponents-core-5.0.x/httpcore5-h2/apidocs/ ${project.url}/httpclient5/apidocs/ org.apache.maven.plugins maven-checkstyle-plugin validate-main validate hc-stylecheck/default.xml hc-stylecheck/asl2.header true true false ${basedir}/src/main checkstyle validate-test validate hc-stylecheck/default.xml hc-stylecheck/asl2.header true true false ${basedir}/src/test checkstyle com.github.siom79.japicmp japicmp-maven-plugin ${project.groupId} ${project.artifactId} ${api.comparison.version} jar ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} true true METHOD_NEW_DEFAULT true true @org.apache.hc.core5.annotation.Internal true verify cmp org.apache.rat apache-rat-plugin verify check src/docbkx/resources/** src/test/resources/*.truststore src/test/resources/*.serialized .checkstyle .externalToolBuilders/** maven-eclipse.xml **/serial **/index.txt maven-project-info-reports-plugin false index dependency-info dependency-management issue-management licenses mailing-lists scm summary maven-javadoc-plugin true true https://hc.apache.org/httpcomponents-core-5.0.x/httpcore5/apidocs/ https://hc.apache.org/httpcomponents-core-5.0.x/httpcore5-h2/apidocs/ ${project.url}/httpclient5/apidocs/ Apache HttpClient org.apache.hc.client5.http* Apache HttpClient Cache org.apache.hc.client5.http.cache*:org.apache.hc.client5.http.impl.cache*:org.apache.hc.client5.http.schedule:org.apache.hc.client5.http.impl.schedule* Apache HttpClient Fluent org.apache.hc.client5.http.fluent* Apache HttpClient Testing org.apache.hc.client5.testing* Apache HttpClient Windows features org.apache.hc.client5.http.impl.win* javadoc aggregate com.github.siom79.japicmp japicmp-maven-plugin cmp-report ${project.groupId} ${project.artifactId} ${api.comparison.version} jar ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} @org.apache.hc.core5.annotation.Internal true maven-jxr-plugin maven-surefire-report-plugin httpcomponents-client-rel-v5.2.1/src/000077500000000000000000000000001434266521000176205ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/src/site/000077500000000000000000000000001434266521000205645ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/src/site/resources/000077500000000000000000000000001434266521000225765ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/src/site/resources/css/000077500000000000000000000000001434266521000233665ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/src/site/resources/css/site.css000066400000000000000000000015141434266521000250450ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ @import url("/css/hc-maven.css"); httpcomponents-client-rel-v5.2.1/src/site/site.xml000066400000000000000000000064231434266521000222570ustar00rootroot00000000000000 org.apache.httpcomponents maven-site-skin 1.1 Apache http://www.apache.org/images/asf_logo_wide.gif http://www.apache.org/ HttpComponents http://hc.apache.org/images/logos/httpcomponents.png http://hc.apache.org/ httpcomponents-client-rel-v5.2.1/test-CA/000077500000000000000000000000001434266521000202715ustar00rootroot00000000000000httpcomponents-client-rel-v5.2.1/test-CA/README.txt000066400000000000000000000004561434266521000217740ustar00rootroot00000000000000This directory contains CA key and certificate for unit and integration tests --- Use this command to check the private key Passphrase: nopassword --- openssl rsa -in ca-key.pem -check -text -noout --- Use this command to print CA certificate details --- openssl x509 -in ca-cert.pem -text -noout ---httpcomponents-client-rel-v5.2.1/test-CA/ca-cert.pem000066400000000000000000000025371434266521000223210ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDyTCCArGgAwIBAgIJAO3mCIu9mboMMA0GCSqGSIb3DQEBCwUAMHoxIzAhBgNV BAoMGkFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMR8wHQYDVQQLDBZIdHRwQ29t cG9uZW50cyBQcm9qZWN0MRAwDgYDVQQDDAdUZXN0IENBMSAwHgYJKoZIhvcNAQkB FhFkZXZAaGMuYXBhY2hlLm9yZzAgFw0xNDEwMTMxNTAxMjBaGA8yMjg4MDcyODE1 MDEyMFowejEjMCEGA1UECgwaQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xHzAd BgNVBAsMFkh0dHBDb21wb25lbnRzIFByb2plY3QxEDAOBgNVBAMMB1Rlc3QgQ0Ex IDAeBgkqhkiG9w0BCQEWEWRldkBoYy5hcGFjaGUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEApXhHtKRvAxbLI+f21zNe68dkVXAhSMIfHQJGb2en S1H8yE4HPIb4vPQ0U7fQCb7RXplm6cHExpof4cO3DmyqD5KeQk0TdM8XrhviDgwj Y0KQ/lgwGHR5CpYoZ6LYWaLSE/wt9dVu80UcK8a3hW9G0X/4b79fMO6HYDix+CI4 b17sqZ4K0tWKA10Xe+2RJU8Y01pPBaPR/UsAn+a1pZ6f8BhL879oWHfLWKcgZOYP U4sYED0S8gs4/ED1zRj2/uHb313sHTl+OU4X5v+OvwBvbNBrl5qfMTZnRNxlOfRq UTJdcopsp2aNeqHiorSDOrHwMIJpxQ2XqHT2l9s8msXf4wIDAQABo1AwTjAdBgNV HQ4EFgQUA+Tn2g9k2xMevYWrdrwpyi+nx0swHwYDVR0jBBgwFoAUA+Tn2g9k2xMe vYWrdrwpyi+nx0swDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAFVEp 8Nv6JoFY7oGgu6068fH/kq7A3SllqMpbv7dddI9fgwy352cBtg6PkYkGtEE037xs FQSYV1NiAkNWTJER+Q+kVbQrhuPNKZqh1g0sUKwv3X20BmgJ9hbU9klWZjdjujyd h9Ybjuntkn5XPp1zN6zHD0sQReEJnRlD6FT1axrQWpICzE4qoo8k64G+6/rqFywc oMc/Of3KCAHjtbWklEu97hjBvGC/nEP4/VhRrjWWSeGHv88LCyO/Yg6v3zrZHFLW +KhsDCPyLxSSISFskLQfukiqf2lr87kQq/oF27sAr3sR3Jqh4qzflM2XLgjmZuRE OrHT6lvUemRyksA5qg== -----END CERTIFICATE----- httpcomponents-client-rel-v5.2.1/test-CA/ca-key.pem000066400000000000000000000034521434266521000221510ustar00rootroot00000000000000-----BEGIN ENCRYPTED PRIVATE KEY----- MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIq0bLh96mWv4CAggA MBQGCCqGSIb3DQMHBAimZqUiELx13QSCBMgaLWrGFqveIzwQUsebS6FBdVq0lodz Vlekje8ycFDYSd21V9jPMwrSupZceeBQjCrpyLZ3oPkR+MvObmznev8XYcJzVCkF E9ApAaHZe248wWcu1/D7auHNG3GyZfvYS0c//Rs2OzMZfsUvX93RVullCRREvCYS qXhaO3ywFocndKRpSnkOBs2SRa0yc9POl4n4dwyKhsJUaMSmhPbJr9UBvCbXHZIA gLcSWzVon3EtZCSubMp9eo90G5MzIXEyPBTcIHwpyqRWTkaTUTq4R0c4/RTX+l7K OZuRIEeBEW6z009fSagymN/KEH3gUkg5pG6i1YWF63OVKTMGn+yQGWwYXwTyEGi5 HZpD98wh3ycucmL93XLk+yYXQcTp1i+u4GaXNWGREQvNW6onCGeg6WWj1PrIsqoi TZ2pgQUJWPR1K3037hY0o9sakAkyYSyTPVvHOUcbf3+GhqGS1FsSNOxKRNpYm/3v Gf0SUN8BavPliK9NSU5JAbprr/hoL5o72dCX9DiOgwfW3HyD/gLh7sVyVBdAzTnE XFaYFnrb5QnqHbgWvaLbJUT5K7MW3OFLVConydYtYdaUl5z49OflhgnvYOPgTSUr k9c7exQjedAduPd8dXODh9l2g+QEXJoT+YYFEYHkQlsZgH1hCLXD1TmAeI4LMklb vPaGE8Ouj1pfbejdTNsqLfW0IiR/jZzEjRgqrueMf2VUjtqTZyPayc2rU4kOoKhv JzQ0wOFhgRztWJy2voRe+iYss3ToqZ7qLpjBfCTsxCJqbuaGeJWWSnOlDpSysgr+ q4BvCzDcvf/0mKD2cQuJx/kynQMCcWB/VegRsQ24Y+3T7IU1w8ccmRfSZ93AwkAh MKJzKaVhD/gn9vUG/we18p7RMIc9pk1o2Z2Ru3mKjkO3QYRP6Y7yk0ah2JKrHIPf LWfPuHmtzHQXkY3RbVvxvwD/+qHm8ogXq52w8cpGhY5UwAEHrLLwypdBHccrAJjo bE13M/MrtTry/k8OMRqhhRzHUXBq6mLaWffCaP2SAVfJEez2iASvGJFvgy3bSkWY rwWMSfZKDkauwDMW5gpFrpeuqgD64LO72sN01riVDpaEyNODRCEEBGce+O+91R9K TLVgRYFsxClyZy1nynD66gkTepEm1yOgcdqV3651Os+TGm39jGYHy1k9mPz8ypqf 8n8uw4nV3SbIwfpy4Z8onHixfc/Fugm7yQHW4dSuCpahyIJHom6Cq7SZfPuo9e3t 8tqaxvK4U/dAXoimvN1eakH2FoVFIj3mk7OAKBgmDINH9GlzXPwRsTfiJSP4Xaod ouWIQLLeXQuuOc5VJd1Xex75o8ciSOomAS0uR4Fvk/2NkAm0EMddjZnuWLQaXPry JiUIgSx3w3yRq9RSQOxDRQpp2nP2roX7cyeGPzTmeujikExGTa3YBxuAShDLx5pt fpi0ol8H8ohDU4eV9pv96KRBG9e8sQf1zpGjeYLTFiN35IQxYJx3HTXp9/oFWkmA OdCEwggIKJ/RtgkWOWogTilQVA41p4XZr661fxoSE86sHXkZKn8IGnAKLFT46nWM IYVDalYUiSNZr+KbzmLIV3LmYE3mlqGI4vDvQtd9zQk/uatYBc2DetuTWPZHCEKS 3Nk= -----END ENCRYPTED PRIVATE KEY----- httpcomponents-client-rel-v5.2.1/test-CA/openssl.cnf000066400000000000000000000260721434266521000224530ustar00rootroot00000000000000# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # # This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = $ENV::HOME/.rnd # Extra OBJECT IDENTIFIER info: #oid_file = $ENV::HOME/.oid oid_section = new_oids # To use this configuration file with the "-extfile" option of the # "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: # extensions = # (Alternatively, use a configuration file that has only # X.509v3 extensions in its main [= default] section.) [ new_oids ] # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 # Policies used by the TSA examples. tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7 #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] dir = ./demoCA # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several ctificates with same subject. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file x509_extensions = usr_cert # The extentions to add to the cert # Comment out the following two lines for the "traditional" # (and highly broken) format. name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. # copy_extensions = copy # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # crlnumber must also be commented out to leave a V1 CRL. # crl_extensions = crl_ext default_days = 365 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = default # use public key default MD preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 2048 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extentions to add to the self signed cert # Passwords for private keys if not present they will be prompted for # input_password = secret # output_password = secret # This sets a mask for permitted string types. There are several options. # default: PrintableString, T61String, BMPString. # pkix : PrintableString, BMPString (PKIX recommendation before 2004) # utf8only: only UTF8Strings (PKIX recommendation after 2004). # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). # MASK:XXXX a literal mask value. # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. string_mask = utf8only # req_extensions = v3_req # The extensions to add to a certificate request [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = Some-State localityName = Locality Name (eg, city) 0.organizationName = Organization Name (eg, company) 0.organizationName_default = Apache Software Foundation organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = HttpComponents Project commonName = Common Name (e.g. server FQDN or YOUR name) commonName_max = 64 emailAddress = Email Address emailAddress_max = 64 # SET-ex3 = SET extension number 3 [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name [ usr_cert ] # These extensions are added when 'ca' signs a request. # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This is required for TSA certificates. # extendedKeyUsage = critical,timeStamping [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment [ v3_ca ] # Extensions for a typical CA # PKIX recommendation. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer # This is what PKIX recommends but some broken software chokes on critical # extensions. #basicConstraints = critical,CA:true # So we do this instead. basicConstraints = CA:true # Key usage: this is typical for a CA certificate. However since it will # prevent it being used as an test self-signed certificate it is best # left out by default. # keyUsage = cRLSign, keyCertSign # Some might want this also # nsCertType = sslCA, emailCA # Include email address in subject alt name: another PKIX recommendation # subjectAltName=email:copy # Copy issuer details # issuerAltName=issuer:copy # DER hex encoding of an extension: beware experts only! # obj=DER:02:03 # Where 'obj' is a standard or added object # You can even override a supported extension: # basicConstraints= critical, DER:30:03:01:01:FF [ crl_ext ] # CRL extensions. # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy authorityKeyIdentifier=keyid:always [ proxy_cert_ext ] # These extensions should be added when creating a proxy certificate # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo #################################################################### [ tsa ] default_tsa = tsa_config1 # the default TSA section [ tsa_config1 ] # These are used by the TSA reply generation only. dir = ./demoCA # TSA root directory serial = $dir/tsaserial # The current serial number (mandatory) crypto_device = builtin # OpenSSL engine to use for signing signer_cert = $dir/tsacert.pem # The TSA signing certificate # (optional) certs = $dir/cacert.pem # Certificate chain to include in reply # (optional) signer_key = $dir/private/tsakey.pem # The TSA private key (optional) default_policy = tsa_policy1 # Policy if request did not specify it # (optional) other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) digests = md5, sha1 # Acceptable message digests (mandatory) accuracy = secs:1, millisecs:500, microsecs:100 # (optional) clock_precision_digits = 0 # number of digits after dot. (optional) ordering = yes # Is ordering defined for timestamps? # (optional, default: no) tsa_name = yes # Must the TSA name be included in the reply? # (optional, default: no) ess_cert_id_chain = no # Must the ESS cert id chain be included? # (optional, default: no)