pax_global_header00006660000000000000000000000064137322353520014517gustar00rootroot0000000000000052 comment=92d86c3d1e6a663a2da295b891d5a9f99bd195d6 clj-http-client-1.2.0/000077500000000000000000000000001373223535200145205ustar00rootroot00000000000000clj-http-client-1.2.0/.gitignore000066400000000000000000000001601373223535200165050ustar00rootroot00000000000000target pom.xml .nrepl-port /.lein* /resources/locales.clj /resources/**/Messages*.class /dev-resources/i18n/bin clj-http-client-1.2.0/.travis.yml000066400000000000000000000015541373223535200166360ustar00rootroot00000000000000language: clojure lein: 2.9.1 jobs: include: - stage: jdk8 script: lein with-profile dev test jdk: openjdk8 #- # same as previous stage # script: lein with-profile fips test # jdk: openjdk8 - stage: jdk11 script: lein with-profile dev test jdk: openjdk11 #- # same as previous stage # script: lein with-profile fips test # jdk: openjdk11 sudo: false # Java needs to be able to resolve its hostname when doing integration style # tests, which it cannot do in certain cases with travis-ci. If we need the # runtime/container to be able to resolve its own hostname we need to use # either the `hostname` or `hosts` "addon" for travis. Since we don't care # what the hostname is, here we just give it a garbage name based on the name # of the project. addons: hostname: cljhttpclient notifications: email: false clj-http-client-1.2.0/CHANGELOG.md000066400000000000000000000160361373223535200163370ustar00rootroot00000000000000## 1.0.0 * Adds `::max-connections-total` and `::max-connections-per-route` to allow the Apache HTTP client values to be overridden. They default to 20 and 2 (to correlate to the Apache client behavior) respectively. * Update to clj-parent 1.7.12. This moves the library to require Java 8. ## 0.9.0 This is a feature release * [TK-443](https://tickets.puppetlabs.com/browse/TK-443) Adds `:enable-url-metrics?` and `setEnableURLMetrics` to the clojure and java APIs respectively. This allows disabling autogenerated url based metrics. * [PE-19979](https://tickets.puppetlabs.com/browse/PE-19979) & [SERVER-1733](https://tickets.puppetlabs.com/browse/SERVER-1733) Update dependencies via clj-parent (i18n dependency specifically). ## 0.8.0 This is a feature release * [PDB-2640](https://tickets.puppetlabs.com/browse/PDB-2640) Added a `:compress-request-body` request option which allows for the request body content to be gzip-compressed before forwarding it on to the server ## 0.7.0 This is a feature release * [SERVER-1556](https://tickets.puppetlabs.com/browse/SERVER-1556) Add puppetlabs/clj-i18n support to clj-http-client. * Pass the global i18n locale binding in the "accept-language" header of Clojure client requests. ## 0.6.0 This is a feature and maintenance release * [TK-179](https://tickets.puppetlabs.com/browse/TK-179) Refactor to eliminate Clojure/Java duplication * [TK-316](https://tickets.puppetlabs.com/browse/TK-316) Add support for metrics * [TK-317](https://tickets.puppetlabs.com/browse/TK-317) Add ability to pass a metrics-id * [TK-354](https://tickets.puppetlabs.com/browse/TK-354) Add [documentation on metrics](./doc/metrics.md) * [TK-308](https://tickets.puppetlabs.com/browse/TK-308) Bump Apache HttpAsyncClient library to 4.1.2 * [TK-402](https://tickets.puppetlabs.com/browse/TK-402) Allow metric namespace to be configurable * Bump Trapperkeeper and Kitchensink dependencies from 1.1.1 to 1.5.1 (TK) and 1.2.0 to 1.3.0 (KS). ## 0.5.0 This is a feature release. * [TK-312](https://tickets.puppetlabs.com/browse/TK-312) Unbuffered streams in Java to match clojure support released in 0.4.5. * Add request function to clojure client protocol. ## 0.4.6 This is a maintenance release. * [TK-303](https://tickets.puppetlabs.com/browse/TK-303) - update dependencies to use Apache httpasyncclient v4.1.1. ## 0.4.5 This is a feature release, and probably should have been 0.5.0 in order to comply with semantic versioning. * Add support for streaming responses in the Clojure API - (ca5ad63) Scott Walker ## 0.4.4 This is a maintenance release. * [TK-196](https://tickets.puppetlabs.com/browse/TK-196) - update prismatic dependencies to the latest versions. ## 0.4.3 This is a feature release. * [TK-134](https://tickets.puppetlabs.com/browse/TK-134) - Introduced two new optional client configuration settings: - `connect-timeout-milliseconds`: maximum number of milliseconds that the client will wait for a connection to be established. - `socket-timeout-milliseconds`: maximum number of milliseconds that the client will allow for no data to be available on the socket before closing the underlying connection, 'SO_TIMEOUT' in socket terms. ## 0.4.2 This is a bugfix release. * [TK-145](https://tickets.puppetlabs.com/browse/TK-145) - Fixed a bug which caused some HTTP requests to incorrectly have `charset=UTF-8` added to their `Content-Type` headers. ## 0.4.1 This is a maintenance release. * Add documentation for making requests using the Java and Clojure clients. * Upgrade jvm-ssl-utils (previously known as jvm-certificate-authority) dependency to 0.7.0. * Upgrade clj-kitchensink dependency to 1.0.0. * Upgrade trapperkeeper dependency to 1.0.1. ## 0.4.0 This is a feature release which has some breaking changes. * Support for non-client bound asynchronous requests has been removed from both the Clojure and Java-layer APIs. This includes all of the request functions that previously existed in the `client.async` Clojure namespace and the request methods in the `AsyncHttpClient` Java class. * Add a Java-layer API for getting an instance of an HttpClient on which multiple requests -- e.g.., GET, POST -- can be made. Clients are created via the `createClient` method on the new `Async` and `Sync` classes, for a client that can make asynchronous or synchronous web requests, respectively. * Non-client bound synchronous requests can still be performed through the Java API but must now be done through the `Sync` classes rather than the `SyncHttpClient` class. The `SyncHttpClient` class is now used as the type of the instance that the `Sync.createClient()` method returns. * The Java `RequestOptions` class was refactored into new `ClientOptions` and `RequestOptions` classes which can be used with the client-bound `Async` and `Sync` APIs. For non-client bound requests, options are now defined via a `SimpleRequestOptions` class. * Reworked connection close behavior to more robustly handle successful and failed connections. ## 0.3.1 This is a bugfix release. * Fix a memory leak that occurred as a result of connection failures. ## 0.3.0 This is a feature release. * Add configuration settings for SSL Protocols and Cipher Suites to both the Java and Clojure clients. ## 0.2.8 This is a bugfix release. * Fix a bug in the Java client API that caused a file descriptor leak each time a client was created for a new request. ## 0.2.7 This is a bugfix release. * Fix a bug where the character encoding was always being set to ISO-8859-1 w/o a charset ever being explicitly specified in the Content-Type header. We now honor the existing charset if there is one in the header, and otherwise we use UTF-8 and explicitly add the charset to the header. ## 0.2.6 * Add :follow-redirects and :force-redirects options to the clojure client. * Add followRedirects and forceRedirects options to the Java client. ## 0.2.5 * Add an overloaded constructor for `RequestOptions` that accepts a String uri ## 0.2.4 * Fix a bug in the Java client API that caused an NPE if a Content-Type header did not specify a charset ## 0.2.3 * No changes ## 0.2.2 * Add back in support for query-params map in Clojure API ## 0.2.1 * Upgrade to Apache HttpAsyncClient v4.0.2 (fixes a bug where headers don't get included when following redirects). ## 0.2.0 * Port the code to use the Apache HttpAsyncClient library instead of http-kit. * The API around creating a persistent client has changed and persistent clients are explicitly managed * The available request options have changed. Some convenience options have been removed. ## 0.1.7 * Explicitly target JDK6 when building release jars ## 0.1.6 * Add support for configuring client SSL context with a CA cert without a client cert/key ## 0.1.5 * Update to latest version of puppetlabs/kitchensink * Use puppetlabs/certificate-authority for SSL tasks ## 0.1.4 * Fix bug in sync.clj when excluding clojure.core/get ## 0.1.3 * Added a Java API for the synchronous client clj-http-client-1.2.0/CODEOWNERS000066400000000000000000000002621373223535200161130ustar00rootroot00000000000000# This will cause the puppetserver-maintainers group to be assigned # review of any opened PRs against the branches containing this file. * @puppetlabs/puppetserver-maintainers clj-http-client-1.2.0/CONTRIBUTING.md000066400000000000000000000010171373223535200167500ustar00rootroot00000000000000# How to contribute Third-party patches are essential for keeping puppet open-source projects great. We want to keep it as easy as possible to contribute changes that allow you to get the most out of our projects. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. For more info, see our canonical guide to contributing: [https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md) clj-http-client-1.2.0/LICENSE000066400000000000000000000260751373223535200155370ustar00rootroot00000000000000Apache 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. clj-http-client-1.2.0/Makefile000066400000000000000000000000441373223535200161560ustar00rootroot00000000000000include dev-resources/Makefile.i18n clj-http-client-1.2.0/README.md000066400000000000000000000032331373223535200160000ustar00rootroot00000000000000# puppetlabs/http-client [![Build Status](https://travis-ci.com/puppetlabs/clj-http-client.png?branch=master)](https://travis-ci.com/puppetlabs/clj-http-client) This is a wrapper around the [Apache HttpAsyncClient library](http://hc.apache.org/httpcomponents-asyncclient-4.0.x/) providing some extra functionality for configuring SSL in a way compatible with Puppet. ## Installation Add the following dependency to your `project.clj` file: [![Clojars Project](http://clojars.org/puppetlabs/http-client/latest-version.svg)](http://clojars.org/puppetlabs/http-client) ## Details Async versions of the http methods are exposed in puppetlabs.http.client.async, and synchronous versions are in puppetlabs.http.client.sync. For information on using these namespaces, see the page on [making requests with clojure clients](doc/clojure-client.md). Additionally, this library allows you to make requests using Java clients. For information on how to do this, see the page on [making requests with java clients](doc/java-client.md). ## Testing The tests require pki files in the `dev-resources/ssl/` directory of: * `ca.pem`: a CA cert with the CN of "puppet" * `key.pem`: a node private key * `cert.pem`: a cert signed by `ca.pem` for the private key at `key.pem` with a CN of "localhost" * `alternate-ca.pem`: a valid but untrusted CA cert The repo contains these files needed for testing, though if needed you may want to read `dev-resources/gen-pki.sh` for the commands to generate additional sets of files. ## Support We use the [Trapperkeeper project on JIRA](https://tickets.puppetlabs.com/browse/TK) for tickets on clj-http-client, although Github issues are welcome too. clj-http-client-1.2.0/dev-resources/000077500000000000000000000000001373223535200173065ustar00rootroot00000000000000clj-http-client-1.2.0/dev-resources/Makefile.i18n000066400000000000000000000141011373223535200215210ustar00rootroot00000000000000# -*- Makefile -*- # This file was generated by the i18n leiningen plugin # Do not edit this file; it will be overwritten the next time you run # lein i18n init # # The name of the package into which the translations bundle will be placed BUNDLE=puppetlabs.http_client # The name of the POT file into which the gettext code strings (msgid) will be placed POT_NAME=http-client.pot # The list of names of packages covered by the translation bundle; # by default it contains a single package - the same where the translations # bundle itself is placed - but this can be overridden - preferably in # the top level Makefile PACKAGES?=$(BUNDLE) LOCALES=$(basename $(notdir $(wildcard locales/*.po))) BUNDLE_DIR=$(subst .,/,$(BUNDLE)) BUNDLE_FILES=$(patsubst %,resources/$(BUNDLE_DIR)/Messages_%.class,$(LOCALES)) FIND_SOURCES=find src -name \*.clj # xgettext before 0.19 does not understand --add-location=file. Even CentOS # 7 ships with an older gettext. We will therefore generate full location # info on those systems, and only file names where xgettext supports it LOC_OPT=$(shell xgettext --add-location=file -f - /dev/null 2>&1 && echo --add-location=file || echo --add-location) LOCALES_CLJ=resources/locales.clj define LOCALES_CLJ_CONTENTS { :locales #{$(patsubst %,"%",$(LOCALES))} :packages [$(patsubst %,"%",$(PACKAGES))] :bundle $(patsubst %,"%",$(BUNDLE).Messages) } endef export LOCALES_CLJ_CONTENTS i18n: msgfmt # Update locales/.pot update-pot: locales/$(POT_NAME) locales/$(POT_NAME): $(shell $(FIND_SOURCES)) | locales @tmp=$$(mktemp $@.tmp.XXXX); \ $(FIND_SOURCES) \ | xgettext --from-code=UTF-8 --language=lisp \ --copyright-holder='Puppet ' \ --package-name="$(BUNDLE)" \ --package-version="$(BUNDLE_VERSION)" \ --msgid-bugs-address="docs@puppet.com" \ -k \ -kmark:1 -ki18n/mark:1 \ -ktrs:1 -ki18n/trs:1 \ -ktru:1 -ki18n/tru:1 \ -ktrun:1,2 -ki18n/trun:1,2 \ -ktrsn:1,2 -ki18n/trsn:1,2 \ $(LOC_OPT) \ --add-comments --sort-by-file \ -o $$tmp -f -; \ sed -i.bak -e 's/charset=CHARSET/charset=UTF-8/' $$tmp; \ sed -i.bak -e 's/POT-Creation-Date: [^\\]*/POT-Creation-Date: /' $$tmp; \ rm -f $$tmp.bak; \ if ! diff -q -I POT-Creation-Date $$tmp $@ >/dev/null 2>&1; then \ mv $$tmp $@; \ else \ rm $$tmp; touch $@; \ fi # Run msgfmt over all .po files to generate Java resource bundles # and create the locales.clj file msgfmt: $(BUNDLE_FILES) $(LOCALES_CLJ) clean-orphaned-bundles # Force rebuild of locales.clj if its contents is not the the desired one. The # shell echo is used to add a trailing newline to match the one from `cat` ifneq ($(shell cat $(LOCALES_CLJ) 2> /dev/null),$(shell echo '$(LOCALES_CLJ_CONTENTS)')) .PHONY: $(LOCALES_CLJ) endif $(LOCALES_CLJ): | resources @echo "Writing $@" @echo "$$LOCALES_CLJ_CONTENTS" > $@ # Remove every resource bundle that wasn't generated from a PO file. # We do this because we used to generate the english bundle directly from the POT. .PHONY: clean-orphaned-bundles clean-orphaned-bundles: @for bundle in resources/$(BUNDLE_DIR)/Messages_*.class; do \ locale=$$(basename "$$bundle" | sed -E -e 's/\$$?1?\.class$$/_class/' | cut -d '_' -f 2;); \ if [ ! -f "locales/$$locale.po" ]; then \ rm "$$bundle"; \ fi \ done resources/$(BUNDLE_DIR)/Messages_%.class: locales/%.po | resources msgfmt --java2 -d resources -r $(BUNDLE).Messages -l $(*F) $< # Use this to initialize translations. Updating the PO files is done # automatically through a CI job that utilizes the scripts in the project's # `bin` file, which themselves come from the `clj-i18n` project. locales/%.po: | locales @if [ ! -f $@ ]; then \ touch $@ && msginit --no-translator -l $(*F) -o $@ -i locales/$(POT_NAME); \ fi resources locales: @mkdir $@ help: $(info $(HELP)) @echo .PHONY: help define HELP This Makefile assists in handling i18n related tasks during development. Files that need to be checked into source control are put into the locales/ directory. They are locales/$(POT_NAME) - the POT file generated by 'make update-pot' locales/$$LANG.po - the translations for $$LANG Only the $$LANG.po files should be edited manually; this is usually done by translators. You can use the following targets: i18n: refresh all the files in locales/ and recompile resources update-pot: extract strings and update locales/$(POT_NAME) locales/LANG.po: create translations for LANG msgfmt: compile the translations into Java classes; this step is needed to make translations available to the Clojure code and produces Java class files in resources/ endef # @todo lutter 2015-04-20: for projects that use libraries with their own # translation, we need to combine all their translations into one big po # file and then run msgfmt over that so that we only have to deal with one # resource bundle clj-http-client-1.2.0/dev-resources/exts.cnf000066400000000000000000000017031373223535200207620ustar00rootroot00000000000000[ req ] #default_bits = 2048 #default_md = sha256 #default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) localityName = Locality Name (eg, city) 0.organizationName = Organization Name (eg, company) organizationalUnitName = Organizational Unit Name (eg, section) commonName = Common Name (eg, fully qualified host name) commonName_max = 64 [ req_attributes ] # This section should be referenced when building an x509v3 CA # Certificate. # The default path length and the key usage can be overridden # modified by setting the CERTPATHLEN and CERTUSAGE environment # variables. [x509v3_CA] subjectKeyIdentifier = hash basicConstraints=critical,CA:true keyUsage=digitalSignature,keyCertSign,cRLSign authorityKeyIdentifier=keyid clj-http-client-1.2.0/dev-resources/gen-pki.sh000077500000000000000000000026201373223535200211770ustar00rootroot00000000000000#!/bin/bash if ! [[ -d dev-resources/ssl ]]; then echo "This script must be called from the root of the project and dev-resources/ssl must already exist" exit 1 fi echo echo "Generating primary self-signed CA" openssl req -x509 \ -newkey rsa:4096 \ -keyout dev-resources/ssl/ca.key \ -out dev-resources/ssl/ca.pem \ -days 1825 -nodes \ -extensions x509v3_CA \ -config dev-resources/exts.cnf \ -subj "/C=US/ST=OR/L=Portland/O=Puppet, Inc/CN=puppet" echo echo "Generating node cert" openssl genrsa -out dev-resources/ssl/key.pem 2048 echo echo "Creating node CSR" openssl req -new -sha256 \ -key dev-resources/ssl/key.pem \ -out dev-resources/ssl/csr.pem \ -subj "/C=US/ST=OR/L=Portland/O=Puppet, Inc/CN=localhost" echo echo "Signing node CSR" openssl x509 -req \ -in dev-resources/ssl/csr.pem \ -CA dev-resources/ssl/ca.pem \ -CAkey dev-resources/ssl/ca.key \ -CAcreateserial \ -out dev-resources/ssl/cert.pem \ -days 1825 -sha256 echo echo "Generating alternate self-signed CA" openssl req -x509 \ -newkey rsa:4096 \ -keyout dev-resources/ssl/alternate-ca.key \ -out dev-resources/ssl/alternate-ca.pem \ -days 1825 -nodes \ -extensions x509v3_CA \ -config dev-resources/exts.cnf \ -subj "/C=US/ST=OR/L=Portland/O=Puppet, Inc/CN=alternate" echo echo "Cleaning up files that will not be used by the tests" rm dev-resources/ssl/{alternate-ca.key,ca.key,ca.srl,csr.pem} clj-http-client-1.2.0/dev-resources/jdk11-fips-security000066400000000000000000001276261373223535200227650ustar00rootroot00000000000000# # This is the "master security properties file". # # An alternate java.security properties file may be specified # from the command line via the system property # # -Djava.security.properties= # # This properties file appends to the master security properties file. # If both properties files specify values for the same key, the value # from the command-line properties file is selected, as it is the last # one loaded. # # Also, if you specify # # -Djava.security.properties== (2 equals), # # then that properties file completely overrides the master security # properties file. # # To disable the ability to specify an additional properties file from # the command line, set the key security.overridePropertiesFile # to false in the master security properties file. It is set to true # by default. # In this file, various security properties are set for use by # java.security classes. This is where users can statically register # Cryptography Package Providers ("providers" for short). The term # "provider" refers to a package or set of packages that supply a # concrete implementation of a subset of the cryptography aspects of # the Java Security API. A provider may, for example, implement one or # more digital signature algorithms or message digest algorithms. # # Each provider must implement a subclass of the Provider class. # To register a provider in this master security properties file, # specify the provider and priority in the format # # security.provider.= # # This declares a provider, and specifies its preference # order n. The preference order is the order in which providers are # searched for requested algorithms (when no specific provider is # requested). The order is 1-based; 1 is the most preferred, followed # by 2, and so on. # # must specify the name of the Provider as passed to its super # class java.security.Provider constructor. This is for providers loaded # through the ServiceLoader mechanism. # # must specify the subclass of the Provider class whose # constructor sets the values of various properties that are required # for the Java Security API to look up the algorithms or other # facilities implemented by the provider. This is for providers loaded # through classpath. # # Note: Providers can be dynamically registered instead by calls to # either the addProvider or insertProviderAt method in the Security # class. # # List of providers and their preference orders (see above): # security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS security.provider.3=SUN security.provider.4=SunRsaSign #security.provider.5=SunEC #security.provider.6=SunJSSE #security.provider.7=SunJCE #security.provider.8=SunJGSS #security.provider.9=SunSASL #security.provider.10=XMLDSig #security.provider.11=SunPCSC #security.provider.12=JdkLDAP #security.provider.13=JdkSASL #security.provider.14=SunPKCS11 # # A list of preferred providers for specific algorithms. These providers will # be searched for matching algorithms before the list of registered providers. # Entries containing errors (parsing, etc) will be ignored. Use the # -Djava.security.debug=jca property to debug these errors. # # The property is a comma-separated list of serviceType.algorithm:provider # entries. The serviceType (example: "MessageDigest") is optional, and if # not specified, the algorithm applies to all service types that support it. # The algorithm is the standard algorithm name or transformation. # Transformations can be specified in their full standard name # (ex: AES/CBC/PKCS5Padding), or as partial matches (ex: AES, AES/CBC). # The provider is the name of the provider. Any provider that does not # also appear in the registered list will be ignored. # # There is a special serviceType for this property only to group a set of # algorithms together. The type is "Group" and is followed by an algorithm # keyword. Groups are to simplify and lessen the entries on the property # line. Current groups are: # Group.SHA2 = SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256 # Group.HmacSHA2 = HmacSHA224, HmacSHA256, HmacSHA384, HmacSHA512 # Group.SHA2RSA = SHA224withRSA, SHA256withRSA, SHA384withRSA, SHA512withRSA # Group.SHA2DSA = SHA224withDSA, SHA256withDSA, SHA384withDSA, SHA512withDSA # Group.SHA2ECDSA = SHA224withECDSA, SHA256withECDSA, SHA384withECDSA, \ # SHA512withECDSA # Group.SHA3 = SHA3-224, SHA3-256, SHA3-384, SHA3-512 # Group.HmacSHA3 = HmacSHA3-224, HmacSHA3-256, HmacSHA3-384, HmacSHA3-512 # # Example: # jdk.security.provider.preferred=AES/GCM/NoPadding:SunJCE, \ # MessageDigest.SHA-256:SUN, Group.HmacSHA2:SunJCE # #jdk.security.provider.preferred= # # Sun Provider SecureRandom seed source. # # Select the primary source of seed data for the "NativePRNG", "SHA1PRNG" # and "DRBG" SecureRandom implementations in the "Sun" provider. # (Other SecureRandom implementations might also use this property.) # # On Unix-like systems (for example, Solaris/Linux/MacOS), the # "NativePRNG", "SHA1PRNG" and "DRBG" implementations obtains seed data from # special device files such as file:/dev/random. # # On Windows systems, specifying the URLs "file:/dev/random" or # "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding # mechanism for SHA1PRNG and DRBG. # # By default, an attempt is made to use the entropy gathering device # specified by the "securerandom.source" Security property. If an # exception occurs while accessing the specified URL: # # NativePRNG: # a default value of /dev/random will be used. If neither # are available, the implementation will be disabled. # "file" is the only currently supported protocol type. # # SHA1PRNG and DRBG: # the traditional system/thread activity algorithm will be used. # # The entropy gathering device can also be specified with the System # property "java.security.egd". For example: # # % java -Djava.security.egd=file:/dev/random MainClass # # Specifying this System property will override the # "securerandom.source" Security property. # # In addition, if "file:/dev/random" or "file:/dev/urandom" is # specified, the "NativePRNG" implementation will be more preferred than # DRBG and SHA1PRNG in the Sun provider. # securerandom.source=file:/dev/random # # A list of known strong SecureRandom implementations. # # To help guide applications in selecting a suitable strong # java.security.SecureRandom implementation, Java distributions should # indicate a list of known strong implementations using the property. # # This is a comma-separated list of algorithm and/or algorithm:provider # entries. # securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN # # Sun provider DRBG configuration and default instantiation request. # # NIST SP 800-90Ar1 lists several DRBG mechanisms. Each can be configured # with a DRBG algorithm name, and can be instantiated with a security strength, # prediction resistance support, etc. This property defines the configuration # and the default instantiation request of "DRBG" SecureRandom implementations # in the SUN provider. (Other DRBG implementations can also use this property.) # Applications can request different instantiation parameters like security # strength, capability, personalization string using one of the # getInstance(...,SecureRandomParameters,...) methods with a # DrbgParameters.Instantiation argument, but other settings such as the # mechanism and DRBG algorithm names are not currently configurable by any API. # # Please note that the SUN implementation of DRBG always supports reseeding. # # The value of this property is a comma-separated list of all configurable # aspects. The aspects can appear in any order but the same aspect can only # appear at most once. Its BNF-style definition is: # # Value: # aspect { "," aspect } # # aspect: # mech_name | algorithm_name | strength | capability | df # # // The DRBG mechanism to use. Default "Hash_DRBG" # mech_name: # "Hash_DRBG" | "HMAC_DRBG" | "CTR_DRBG" # # // The DRBG algorithm name. The "SHA-***" names are for Hash_DRBG and # // HMAC_DRBG, default "SHA-256". The "AES-***" names are for CTR_DRBG, # // default "AES-128" when using the limited cryptographic or "AES-256" # // when using the unlimited. # algorithm_name: # "SHA-224" | "SHA-512/224" | "SHA-256" | # "SHA-512/256" | "SHA-384" | "SHA-512" | # "AES-128" | "AES-192" | "AES-256" # # // Security strength requested. Default "128" # strength: # "112" | "128" | "192" | "256" # # // Prediction resistance and reseeding request. Default "none" # // "pr_and_reseed" - Both prediction resistance and reseeding # // support requested # // "reseed_only" - Only reseeding support requested # // "none" - Neither prediction resistance not reseeding # // support requested # pr: # "pr_and_reseed" | "reseed_only" | "none" # # // Whether a derivation function should be used. only applicable # // to CTR_DRBG. Default "use_df" # df: # "use_df" | "no_df" # # Examples, # securerandom.drbg.config=Hash_DRBG,SHA-224,112,none # securerandom.drbg.config=CTR_DRBG,AES-256,192,pr_and_reseed,use_df # # The default value is an empty string, which is equivalent to # securerandom.drbg.config=Hash_DRBG,SHA-256,128,none # securerandom.drbg.config= # # Class to instantiate as the javax.security.auth.login.Configuration # provider. # login.configuration.provider=sun.security.provider.ConfigFile # # Default login configuration file # #login.config.url.1=file:${user.home}/.java.login.config # # Class to instantiate as the system Policy. This is the name of the class # that will be used as the Policy object. The system class loader is used to # locate this class. # policy.provider=sun.security.provider.PolicyFile # The default is to have a single system-wide policy file, # and a policy file in the user's home directory. # policy.url.1=file:${java.home}/conf/security/java.policy policy.url.2=file:${user.home}/.java.policy # whether or not we expand properties in the policy file # if this is set to false, properties (${...}) will not be expanded in policy # files. # policy.expandProperties=true # whether or not we allow an extra policy to be passed on the command line # with -Djava.security.policy=somefile. Comment out this line to disable # this feature. # policy.allowSystemProperty=true # whether or not we look into the IdentityScope for trusted Identities # when encountering a 1.1 signed JAR file. If the identity is found # and is trusted, we grant it AllPermission. Note: the default policy # provider (sun.security.provider.PolicyFile) does not support this property. # policy.ignoreIdentityScope=false # # Default keystore type. # keystore.type=pkcs12 # # Controls compatibility mode for JKS and PKCS12 keystore types. # # When set to 'true', both JKS and PKCS12 keystore types support loading # keystore files in either JKS or PKCS12 format. When set to 'false' the # JKS keystore type supports loading only JKS keystore files and the PKCS12 # keystore type supports loading only PKCS12 keystore files. # keystore.type.compat=true # # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when passed to the # SecurityManager::checkPackageAccess method unless the corresponding # RuntimePermission("accessClassInPackage."+package) has been granted. # package.access=sun.misc.,\ sun.reflect.,\ org.GNOME.Accessibility. # # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when passed to the # SecurityManager::checkPackageDefinition method unless the corresponding # RuntimePermission("defineClassInPackage."+package) has been granted. # # By default, none of the class loaders supplied with the JDK call # checkPackageDefinition. # package.definition=sun.misc.,\ sun.reflect. # # Determines whether this properties file can be appended to # or overridden on the command line via -Djava.security.properties # security.overridePropertiesFile=true # # Determines the default key and trust manager factory algorithms for # the javax.net.ssl package. # ssl.KeyManagerFactory.algorithm=BCFKS ssl.TrustManagerFactory.algorithm=PKIX # # The Java-level namelookup cache policy for successful lookups: # # any negative value: caching forever # any positive value: the number of seconds to cache an address for # zero: do not cache # # default value is forever (FOREVER). For security reasons, this # caching is made forever when a security manager is set. When a security # manager is not set, the default behavior in this implementation # is to cache for 30 seconds. # # NOTE: setting this to anything other than the default value can have # serious security implications. Do not set it unless # you are sure you are not exposed to DNS spoofing attack. # #networkaddress.cache.ttl=-1 # The Java-level namelookup cache policy for failed lookups: # # any negative value: cache forever # any positive value: the number of seconds to cache negative lookup results # zero: do not cache # # In some Microsoft Windows networking environments that employ # the WINS name service in addition to DNS, name service lookups # that fail may take a noticeably long time to return (approx. 5 seconds). # For this reason the default caching policy is to maintain these # results for 10 seconds. # networkaddress.cache.negative.ttl=10 # # Properties to configure OCSP for certificate revocation checking # # Enable OCSP # # By default, OCSP is not used for certificate revocation checking. # This property enables the use of OCSP when set to the value "true". # # NOTE: SocketPermission is required to connect to an OCSP responder. # # Example, # ocsp.enable=true # # Location of the OCSP responder # # By default, the location of the OCSP responder is determined implicitly # from the certificate being validated. This property explicitly specifies # the location of the OCSP responder. The property is used when the # Authority Information Access extension (defined in RFC 5280) is absent # from the certificate or when it requires overriding. # # Example, # ocsp.responderURL=http://ocsp.example.net:80 # # Subject name of the OCSP responder's certificate # # By default, the certificate of the OCSP responder is that of the issuer # of the certificate being validated. This property identifies the certificate # of the OCSP responder when the default does not apply. Its value is a string # distinguished name (defined in RFC 2253) which identifies a certificate in # the set of certificates supplied during cert path validation. In cases where # the subject name alone is not sufficient to uniquely identify the certificate # then both the "ocsp.responderCertIssuerName" and # "ocsp.responderCertSerialNumber" properties must be used instead. When this # property is set then those two properties are ignored. # # Example, # ocsp.responderCertSubjectName=CN=OCSP Responder, O=XYZ Corp # # Issuer name of the OCSP responder's certificate # # By default, the certificate of the OCSP responder is that of the issuer # of the certificate being validated. This property identifies the certificate # of the OCSP responder when the default does not apply. Its value is a string # distinguished name (defined in RFC 2253) which identifies a certificate in # the set of certificates supplied during cert path validation. When this # property is set then the "ocsp.responderCertSerialNumber" property must also # be set. When the "ocsp.responderCertSubjectName" property is set then this # property is ignored. # # Example, # ocsp.responderCertIssuerName=CN=Enterprise CA, O=XYZ Corp # # Serial number of the OCSP responder's certificate # # By default, the certificate of the OCSP responder is that of the issuer # of the certificate being validated. This property identifies the certificate # of the OCSP responder when the default does not apply. Its value is a string # of hexadecimal digits (colon or space separators may be present) which # identifies a certificate in the set of certificates supplied during cert path # validation. When this property is set then the "ocsp.responderCertIssuerName" # property must also be set. When the "ocsp.responderCertSubjectName" property # is set then this property is ignored. # # Example, # ocsp.responderCertSerialNumber=2A:FF:00 # # Policy for failed Kerberos KDC lookups: # # When a KDC is unavailable (network error, service failure, etc), it is # put inside a blacklist and accessed less often for future requests. The # value (case-insensitive) for this policy can be: # # tryLast # KDCs in the blacklist are always tried after those not on the list. # # tryLess[:max_retries,timeout] # KDCs in the blacklist are still tried by their order in the configuration, # but with smaller max_retries and timeout values. max_retries and timeout # are optional numerical parameters (default 1 and 5000, which means once # and 5 seconds). Please notes that if any of the values defined here is # more than what is defined in krb5.conf, it will be ignored. # # Whenever a KDC is detected as available, it is removed from the blacklist. # The blacklist is reset when krb5.conf is reloaded. You can add # refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is # reloaded whenever a JAAS authentication is attempted. # # Example, # krb5.kdc.bad.policy = tryLast # krb5.kdc.bad.policy = tryLess:2,2000 # krb5.kdc.bad.policy = tryLast # # Algorithm restrictions for certification path (CertPath) processing # # In some environments, certain algorithms or key lengths may be undesirable # for certification path building and validation. For example, "MD2" is # generally no longer considered to be a secure hash algorithm. This section # describes the mechanism for disabling algorithms based on algorithm name # and/or key length. This includes algorithms used in certificates, as well # as revocation information such as CRLs and signed OCSP Responses. # The syntax of the disabled algorithm string is described as follows: # DisabledAlgorithms: # " DisabledAlgorithm { , DisabledAlgorithm } " # # DisabledAlgorithm: # AlgorithmName [Constraint] { '&' Constraint } # # AlgorithmName: # (see below) # # Constraint: # KeySizeConstraint | CAConstraint | DenyAfterConstraint | # UsageConstraint # # KeySizeConstraint: # keySize Operator KeyLength # # Operator: # <= | < | == | != | >= | > # # KeyLength: # Integer value of the algorithm's key length in bits # # CAConstraint: # jdkCA # # DenyAfterConstraint: # denyAfter YYYY-MM-DD # # UsageConstraint: # usage [TLSServer] [TLSClient] [SignedJAR] # # The "AlgorithmName" is the standard algorithm name of the disabled # algorithm. See "Java Cryptography Architecture Standard Algorithm Name # Documentation" for information about Standard Algorithm Names. Matching # is performed using a case-insensitive sub-element matching rule. (For # example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and # "ECDSA" for signatures.) If the assertion "AlgorithmName" is a # sub-element of the certificate algorithm name, the algorithm will be # rejected during certification path building and validation. For example, # the assertion algorithm name "DSA" will disable all certificate algorithms # that rely on DSA, such as NONEwithDSA, SHA1withDSA. However, the assertion # will not disable algorithms related to "ECDSA". # # A "Constraint" defines restrictions on the keys and/or certificates for # a specified AlgorithmName: # # KeySizeConstraint: # keySize Operator KeyLength # The constraint requires a key of a valid size range if the # "AlgorithmName" is of a key algorithm. The "KeyLength" indicates # the key size specified in number of bits. For example, # "RSA keySize <= 1024" indicates that any RSA key with key size less # than or equal to 1024 bits should be disabled, and # "RSA keySize < 1024, RSA keySize > 2048" indicates that any RSA key # with key size less than 1024 or greater than 2048 should be disabled. # This constraint is only used on algorithms that have a key size. # # CAConstraint: # jdkCA # This constraint prohibits the specified algorithm only if the # algorithm is used in a certificate chain that terminates at a marked # trust anchor in the lib/security/cacerts keystore. If the jdkCA # constraint is not set, then all chains using the specified algorithm # are restricted. jdkCA may only be used once in a DisabledAlgorithm # expression. # Example: To apply this constraint to SHA-1 certificates, include # the following: "SHA1 jdkCA" # # DenyAfterConstraint: # denyAfter YYYY-MM-DD # This constraint prohibits a certificate with the specified algorithm # from being used after the date regardless of the certificate's # validity. JAR files that are signed and timestamped before the # constraint date with certificates containing the disabled algorithm # will not be restricted. The date is processed in the UTC timezone. # This constraint can only be used once in a DisabledAlgorithm # expression. # Example: To deny usage of RSA 2048 bit certificates after Feb 3 2020, # use the following: "RSA keySize == 2048 & denyAfter 2020-02-03" # # UsageConstraint: # usage [TLSServer] [TLSClient] [SignedJAR] # This constraint prohibits the specified algorithm for # a specified usage. This should be used when disabling an algorithm # for all usages is not practical. 'TLSServer' restricts the algorithm # in TLS server certificate chains when server authentication is # performed. 'TLSClient' restricts the algorithm in TLS client # certificate chains when client authentication is performed. # 'SignedJAR' constrains use of certificates in signed jar files. # The usage type follows the keyword and more than one usage type can # be specified with a whitespace delimiter. # Example: "SHA1 usage TLSServer TLSClient" # # When an algorithm must satisfy more than one constraint, it must be # delimited by an ampersand '&'. For example, to restrict certificates in a # chain that terminate at a distribution provided trust anchor and contain # RSA keys that are less than or equal to 1024 bits, add the following # constraint: "RSA keySize <= 1024 & jdkCA". # # All DisabledAlgorithms expressions are processed in the order defined in the # property. This requires lower keysize constraints to be specified # before larger keysize constraints of the same algorithm. For example: # "RSA keySize < 1024 & jdkCA, RSA keySize < 2048". # # Note: The algorithm restrictions do not apply to trust anchors or # self-signed certificates. # # Note: This property is currently used by Oracle's PKIX implementation. It # is not guaranteed to be examined and used by other implementations. # # Example: # jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048 # # jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \ RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224 # # Algorithm restrictions for signed JAR files # # In some environments, certain algorithms or key lengths may be undesirable # for signed JAR validation. For example, "MD2" is generally no longer # considered to be a secure hash algorithm. This section describes the # mechanism for disabling algorithms based on algorithm name and/or key length. # JARs signed with any of the disabled algorithms or key sizes will be treated # as unsigned. # # The syntax of the disabled algorithm string is described as follows: # DisabledAlgorithms: # " DisabledAlgorithm { , DisabledAlgorithm } " # # DisabledAlgorithm: # AlgorithmName [Constraint] { '&' Constraint } # # AlgorithmName: # (see below) # # Constraint: # KeySizeConstraint | DenyAfterConstraint # # KeySizeConstraint: # keySize Operator KeyLength # # DenyAfterConstraint: # denyAfter YYYY-MM-DD # # Operator: # <= | < | == | != | >= | > # # KeyLength: # Integer value of the algorithm's key length in bits # # Note: This property is currently used by the JDK Reference # implementation. It is not guaranteed to be examined and used by other # implementations. # # See "jdk.certpath.disabledAlgorithms" for syntax descriptions. # jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ DSA keySize < 1024 # # Algorithm restrictions for Secure Socket Layer/Transport Layer Security # (SSL/TLS/DTLS) processing # # In some environments, certain algorithms or key lengths may be undesirable # when using SSL/TLS/DTLS. This section describes the mechanism for disabling # algorithms during SSL/TLS/DTLS security parameters negotiation, including # protocol version negotiation, cipher suites selection, peer authentication # and key exchange mechanisms. # # Disabled algorithms will not be negotiated for SSL/TLS connections, even # if they are enabled explicitly in an application. # # For PKI-based peer authentication and key exchange mechanisms, this list # of disabled algorithms will also be checked during certification path # building and validation, including algorithms used in certificates, as # well as revocation information such as CRLs and signed OCSP Responses. # This is in addition to the jdk.certpath.disabledAlgorithms property above. # # See the specification of "jdk.certpath.disabledAlgorithms" for the # syntax of the disabled algorithm string. # # Note: The algorithm restrictions do not apply to trust anchors or # self-signed certificates. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # # Example: # jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \ EC keySize < 224, 3DES_EDE_CBC, anon, NULL # # Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) # processing in JSSE implementation. # # In some environments, a certain algorithm may be undesirable but it # cannot be disabled because of its use in legacy applications. Legacy # algorithms may still be supported, but applications should not use them # as the security strength of legacy algorithms are usually not strong enough # in practice. # # During SSL/TLS security parameters negotiation, legacy algorithms will # not be negotiated unless there are no other candidates. # # The syntax of the legacy algorithms string is described as this Java # BNF-style: # LegacyAlgorithms: # " LegacyAlgorithm { , LegacyAlgorithm } " # # LegacyAlgorithm: # AlgorithmName (standard JSSE algorithm name) # # See the specification of security property "jdk.certpath.disabledAlgorithms" # for the syntax and description of the "AlgorithmName" notation. # # Per SSL/TLS specifications, cipher suites have the form: # SSL_KeyExchangeAlg_WITH_CipherAlg_MacAlg # or # TLS_KeyExchangeAlg_WITH_CipherAlg_MacAlg # # For example, the cipher suite TLS_RSA_WITH_AES_128_CBC_SHA uses RSA as the # key exchange algorithm, AES_128_CBC (128 bits AES cipher algorithm in CBC # mode) as the cipher (encryption) algorithm, and SHA-1 as the message digest # algorithm for HMAC. # # The LegacyAlgorithm can be one of the following standard algorithm names: # 1. JSSE cipher suite name, e.g., TLS_RSA_WITH_AES_128_CBC_SHA # 2. JSSE key exchange algorithm name, e.g., RSA # 3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC # 4. JSSE message digest algorithm name, e.g., SHA # # See SSL/TLS specifications and "Java Cryptography Architecture Standard # Algorithm Name Documentation" for information about the algorithm names. # # Note: If a legacy algorithm is also restricted through the # jdk.tls.disabledAlgorithms property or the # java.security.AlgorithmConstraints API (See # javax.net.ssl.SSLParameters.setAlgorithmConstraints()), # then the algorithm is completely disabled and will not be negotiated. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # There is no guarantee the property will continue to exist or be of the # same syntax in future releases. # # Example: # jdk.tls.legacyAlgorithms=DH_anon, DES_CBC, SSL_RSA_WITH_RC4_128_MD5 # jdk.tls.legacyAlgorithms= \ K_NULL, C_NULL, M_NULL, \ DH_anon, ECDH_anon, \ RC4_128, RC4_40, DES_CBC, DES40_CBC, \ 3DES_EDE_CBC # # The pre-defined default finite field Diffie-Hellman ephemeral (DHE) # parameters for Transport Layer Security (SSL/TLS/DTLS) processing. # # In traditional SSL/TLS/DTLS connections where finite field DHE parameters # negotiation mechanism is not used, the server offers the client group # parameters, base generator g and prime modulus p, for DHE key exchange. # It is recommended to use dynamic group parameters. This property defines # a mechanism that allows you to specify custom group parameters. # # The syntax of this property string is described as this Java BNF-style: # DefaultDHEParameters: # DefinedDHEParameters { , DefinedDHEParameters } # # DefinedDHEParameters: # "{" DHEPrimeModulus , DHEBaseGenerator "}" # # DHEPrimeModulus: # HexadecimalDigits # # DHEBaseGenerator: # HexadecimalDigits # # HexadecimalDigits: # HexadecimalDigit { HexadecimalDigit } # # HexadecimalDigit: one of # 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f # # Whitespace characters are ignored. # # The "DefinedDHEParameters" defines the custom group parameters, prime # modulus p and base generator g, for a particular size of prime modulus p. # The "DHEPrimeModulus" defines the hexadecimal prime modulus p, and the # "DHEBaseGenerator" defines the hexadecimal base generator g of a group # parameter. It is recommended to use safe primes for the custom group # parameters. # # If this property is not defined or the value is empty, the underlying JSSE # provider's default group parameter is used for each connection. # # If the property value does not follow the grammar, or a particular group # parameter is not valid, the connection will fall back and use the # underlying JSSE provider's default group parameter. # # Note: This property is currently used by OpenJDK's JSSE implementation. It # is not guaranteed to be examined and used by other implementations. # # Example: # jdk.tls.server.defaultDHEParameters= # { \ # FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 \ # 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD \ # EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 \ # E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED \ # EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 \ # FFFFFFFF FFFFFFFF, 2} # # TLS key limits on symmetric cryptographic algorithms # # This security property sets limits on algorithms key usage in TLS 1.3. # When the amount of data encrypted exceeds the algorithm value listed below, # a KeyUpdate message will trigger a key change. This is for symmetric ciphers # with TLS 1.3 only. # # The syntax for the property is described below: # KeyLimits: # " KeyLimit { , KeyLimit } " # # WeakKeyLimit: # AlgorithmName Action Length # # AlgorithmName: # A full algorithm transformation. # # Action: # KeyUpdate # # Length: # The amount of encrypted data in a session before the Action occurs # This value may be an integer value in bytes, or as a power of two, 2^29. # # KeyUpdate: # The TLS 1.3 KeyUpdate handshake process begins when the Length amount # is fulfilled. # # Note: This property is currently used by OpenJDK's JSSE implementation. It # is not guaranteed to be examined and used by other implementations. # jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37 # # Cryptographic Jurisdiction Policy defaults # # Import and export control rules on cryptographic software vary from # country to country. By default, Java provides two different sets of # cryptographic policy files[1]: # # unlimited: These policy files contain no restrictions on cryptographic # strengths or algorithms # # limited: These policy files contain more restricted cryptographic # strengths # # The default setting is determined by the value of the "crypto.policy" # Security property below. If your country or usage requires the # traditional restrictive policy, the "limited" Java cryptographic # policy is still available and may be appropriate for your environment. # # If you have restrictions that do not fit either use case mentioned # above, Java provides the capability to customize these policy files. # The "crypto.policy" security property points to a subdirectory # within /conf/security/policy/ which can be customized. # Please see the /conf/security/policy/README.txt file or consult # the Java Security Guide/JCA documentation for more information. # # YOU ARE ADVISED TO CONSULT YOUR EXPORT/IMPORT CONTROL COUNSEL OR ATTORNEY # TO DETERMINE THE EXACT REQUIREMENTS. # # [1] Please note that the JCE for Java SE, including the JCE framework, # cryptographic policy files, and standard JCE providers provided with # the Java SE, have been reviewed and approved for export as mass market # encryption item by the US Bureau of Industry and Security. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # crypto.policy=unlimited # # The policy for the XML Signature secure validation mode. The mode is # enabled by setting the property "org.jcp.xml.dsig.secureValidation" to # true with the javax.xml.crypto.XMLCryptoContext.setProperty() method, # or by running the code with a SecurityManager. # # Policy: # Constraint {"," Constraint } # Constraint: # AlgConstraint | MaxTransformsConstraint | MaxReferencesConstraint | # ReferenceUriSchemeConstraint | KeySizeConstraint | OtherConstraint # AlgConstraint # "disallowAlg" Uri # MaxTransformsConstraint: # "maxTransforms" Integer # MaxReferencesConstraint: # "maxReferences" Integer # ReferenceUriSchemeConstraint: # "disallowReferenceUriSchemes" String { String } # KeySizeConstraint: # "minKeySize" KeyAlg Integer # OtherConstraint: # "noDuplicateIds" | "noRetrievalMethodLoops" # # For AlgConstraint, Uri is the algorithm URI String that is not allowed. # See the XML Signature Recommendation for more information on algorithm # URI Identifiers. For KeySizeConstraint, KeyAlg is the standard algorithm # name of the key type (ex: "RSA"). If the MaxTransformsConstraint, # MaxReferencesConstraint or KeySizeConstraint (for the same key type) is # specified more than once, only the last entry is enforced. # # Note: This property is currently used by the JDK Reference implementation. It # is not guaranteed to be examined and used by other implementations. # jdk.xml.dsig.secureValidationPolicy=\ disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\ maxTransforms 5,\ maxReferences 30,\ disallowReferenceUriSchemes file http https,\ minKeySize RSA 1024,\ minKeySize DSA 1024,\ minKeySize EC 224,\ noDuplicateIds,\ noRetrievalMethodLoops # # Serialization process-wide filter # # A filter, if configured, is used by java.io.ObjectInputStream during # deserialization to check the contents of the stream. # A filter is configured as a sequence of patterns, each pattern is either # matched against the name of a class in the stream or defines a limit. # Patterns are separated by ";" (semicolon). # Whitespace is significant and is considered part of the pattern. # # If the system property jdk.serialFilter is also specified, it supersedes # the security property value defined here. # # If a pattern includes a "=", it sets a limit. # If a limit appears more than once the last value is used. # Limits are checked before classes regardless of the order in the # sequence of patterns. # If any of the limits are exceeded, the filter status is REJECTED. # # maxdepth=value - the maximum depth of a graph # maxrefs=value - the maximum number of internal references # maxbytes=value - the maximum number of bytes in the input stream # maxarray=value - the maximum array length allowed # # Other patterns, from left to right, match the class or package name as # returned from Class.getName. # If the class is an array type, the class or package to be matched is the # element type. # Arrays of any number of dimensions are treated the same as the element type. # For example, a pattern of "!example.Foo", rejects creation of any instance or # array of example.Foo. # # If the pattern starts with "!", the status is REJECTED if the remaining # pattern is matched; otherwise the status is ALLOWED if the pattern matches. # If the pattern contains "/", the non-empty prefix up to the "/" is the # module name; # if the module name matches the module name of the class then # the remaining pattern is matched with the class name. # If there is no "/", the module name is not compared. # If the pattern ends with ".**" it matches any class in the package and all # subpackages. # If the pattern ends with ".*" it matches any class in the package. # If the pattern ends with "*", it matches any class with the pattern as a # prefix. # If the pattern is equal to the class name, it matches. # Otherwise, the status is UNDECIDED. # #jdk.serialFilter=pattern;pattern # # RMI Registry Serial Filter # # The filter pattern uses the same format as jdk.serialFilter. # This filter can override the builtin filter if additional types need to be # allowed or rejected from the RMI Registry or to decrease limits but not # to increase limits. # If the limits (maxdepth, maxrefs, or maxbytes) are exceeded, the object is rejected. # # Each non-array type is allowed or rejected if it matches one of the patterns, # evaluated from left to right, and is otherwise allowed. Arrays of any # component type, including subarrays and arrays of primitives, are allowed. # # Array construction of any component type, including subarrays and arrays of # primitives, are allowed unless the length is greater than the maxarray limit. # The filter is applied to each array element. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # # The built-in filter allows subclasses of allowed classes and # can approximately be represented as the pattern: # #sun.rmi.registry.registryFilter=\ # maxarray=1000000;\ # maxdepth=20;\ # java.lang.String;\ # java.lang.Number;\ # java.lang.reflect.Proxy;\ # java.rmi.Remote;\ # sun.rmi.server.UnicastRef;\ # sun.rmi.server.RMIClientSocketFactory;\ # sun.rmi.server.RMIServerSocketFactory;\ # java.rmi.activation.ActivationID;\ # java.rmi.server.UID # # RMI Distributed Garbage Collector (DGC) Serial Filter # # The filter pattern uses the same format as jdk.serialFilter. # This filter can override the builtin filter if additional types need to be # allowed or rejected from the RMI DGC. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # # The builtin DGC filter can approximately be represented as the filter pattern: # #sun.rmi.transport.dgcFilter=\ # java.rmi.server.ObjID;\ # java.rmi.server.UID;\ # java.rmi.dgc.VMID;\ # java.rmi.dgc.Lease;\ # maxdepth=5;maxarray=10000 # CORBA ORBIorTypeCheckRegistryFilter # Type check enhancement for ORB::string_to_object processing # # An IOR type check filter, if configured, is used by an ORB during # an ORB::string_to_object invocation to check the veracity of the type encoded # in the ior string. # # The filter pattern consists of a semi-colon separated list of class names. # The configured list contains the binary class names of the IDL interface types # corresponding to the IDL stub class to be instantiated. # As such, a filter specifies a list of IDL stub classes that will be # allowed by an ORB when an ORB::string_to_object is invoked. # It is used to specify a white list configuration of acceptable # IDL stub types which may be contained in a stringified IOR # parameter passed as input to an ORB::string_to_object method. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # #com.sun.CORBA.ORBIorTypeCheckRegistryFilter=binary_class_name;binary_class_name # # JCEKS Encrypted Key Serial Filter # # This filter, if configured, is used by the JCEKS KeyStore during the # deserialization of the encrypted Key object stored inside a key entry. # If not configured or the filter result is UNDECIDED (i.e. none of the patterns # matches), the filter configured by jdk.serialFilter will be consulted. # # If the system property jceks.key.serialFilter is also specified, it supersedes # the security property value defined here. # # The filter pattern uses the same format as jdk.serialFilter. The default # pattern allows java.lang.Enum, java.security.KeyRep, java.security.KeyRep$Type, # and javax.crypto.spec.SecretKeySpec and rejects all the others. jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\ java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!* # # Enhanced exception message information # # By default, exception messages should not include potentially sensitive # information such as file names, host names, or port numbers. This property # accepts one or more comma separated values, each of which represents a # category of enhanced exception message information to enable. Values are # case-insensitive. Leading and trailing whitespaces, surrounding each value, # are ignored. Unknown values are ignored. # # NOTE: Use caution before setting this property. Setting this property # exposes sensitive information in Exceptions, which could, for example, # propagate to untrusted code or be emitted in stack traces that are # inadvertently disclosed and made accessible over a public network. # # The categories are: # # hostInfo - IOExceptions thrown by java.net.Socket and the socket types in the # java.nio.channels package will contain enhanced exception # message information # # The property setting in this file can be overridden by a system property of # the same name, with the same syntax and possible values. # #jdk.includeInExceptions=hostInfo # # Policies for distrusting Certificate Authorities (CAs). # # This is a comma separated value of one or more case-sensitive strings, each # of which represents a policy for determining if a CA should be distrusted. # The supported values are: # # SYMANTEC_TLS : Distrust TLS Server certificates anchored by a Symantec # root CA and issued after April 16, 2019 unless issued by one of the # following subordinate CAs which have a later distrust date: # 1. Apple IST CA 2 - G1, SHA-256 fingerprint: # AC2B922ECFD5E01711772FEA8ED372DE9D1E2245FCE3F57A9CDBEC77296A424B # Distrust after December 31, 2019. # 2. Apple IST CA 8 - G1, SHA-256 fingerprint: # A4FE7C7F15155F3F0AEF7AAA83CF6E06DEB97CA3F909DF920AC1490882D488ED # Distrust after December 31, 2019. # # Leading and trailing whitespace surrounding each value are ignored. # Unknown values are ignored. If the property is commented out or set to the # empty String, no policies are enforced. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be supported by other SE implementations. Also, this # property does not override other security properties which can restrict # certificates such as jdk.tls.disabledAlgorithms or # jdk.certpath.disabledAlgorithms; those restrictions are still enforced even # if this property is not enabled. # jdk.security.caDistrustPolicies=SYMANTEC_TLS clj-http-client-1.2.0/dev-resources/jdk8-fips-security000066400000000000000000001225751373223535200227110ustar00rootroot00000000000000# # This is the "master security properties file". # # An alternate java.security properties file may be specified # from the command line via the system property # # -Djava.security.properties= # # This properties file appends to the master security properties file. # If both properties files specify values for the same key, the value # from the command-line properties file is selected, as it is the last # one loaded. # # Also, if you specify # # -Djava.security.properties== (2 equals), # # then that properties file completely overrides the master security # properties file. # # To disable the ability to specify an additional properties file from # the command line, set the key security.overridePropertiesFile # to false in the master security properties file. It is set to true # by default. # In this file, various security properties are set for use by # java.security classes. This is where users can statically register # Cryptography Package Providers ("providers" for short). The term # "provider" refers to a package or set of packages that supply a # concrete implementation of a subset of the cryptography aspects of # the Java Security API. A provider may, for example, implement one or # more digital signature algorithms or message digest algorithms. # # Each provider must implement a subclass of the Provider class. # To register a provider in this master security properties file, # specify the Provider subclass name and priority in the format # # security.provider.= # # This declares a provider, and specifies its preference # order n. The preference order is the order in which providers are # searched for requested algorithms (when no specific provider is # requested). The order is 1-based; 1 is the most preferred, followed # by 2, and so on. # # must specify the subclass of the Provider class whose # constructor sets the values of various properties that are required # for the Java Security API to look up the algorithms or other # facilities implemented by the provider. # # There must be at least one provider specification in java.security. # There is a default provider that comes standard with the JDK. It # is called the "SUN" provider, and its Provider subclass # named Sun appears in the sun.security.provider package. Thus, the # "SUN" provider is registered via the following: # # security.provider.1=sun.security.provider.Sun # # (The number 1 is used for the default provider.) # # Note: Providers can be dynamically registered instead by calls to # either the addProvider or insertProviderAt method in the Security # class. # # List of providers and their preference orders (see above): # security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS security.provider.3=sun.security.provider.Sun security.provider.4=sun.security.rsa.SunRsaSign #security.provider.5=sun.security.ec.SunEC #security.provider.6=com.sun.net.ssl.internal.ssl.Provider #security.provider.7=com.sun.crypto.provider.SunJCE #security.provider.8=sun.security.jgss.SunProvider #security.provider.9=com.sun.security.sasl.Provider #security.provider.10=org.jcp.xml.dsig.internal.dom.XMLDSigRI #security.provider.11=sun.security.smartcardio.SunPCSC # # Sun Provider SecureRandom seed source. # # Select the primary source of seed data for the "SHA1PRNG" and # "NativePRNG" SecureRandom implementations in the "Sun" provider. # (Other SecureRandom implementations might also use this property.) # # On Unix-like systems (for example, Solaris/Linux/MacOS), the # "NativePRNG" and "SHA1PRNG" implementations obtains seed data from # special device files such as file:/dev/random. # # On Windows systems, specifying the URLs "file:/dev/random" or # "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding # mechanism for SHA1PRNG. # # By default, an attempt is made to use the entropy gathering device # specified by the "securerandom.source" Security property. If an # exception occurs while accessing the specified URL: # # SHA1PRNG: # the traditional system/thread activity algorithm will be used. # # NativePRNG: # a default value of /dev/random will be used. If neither # are available, the implementation will be disabled. # "file" is the only currently supported protocol type. # # The entropy gathering device can also be specified with the System # property "java.security.egd". For example: # # % java -Djava.security.egd=file:/dev/random MainClass # # Specifying this System property will override the # "securerandom.source" Security property. # # In addition, if "file:/dev/random" or "file:/dev/urandom" is # specified, the "NativePRNG" implementation will be more preferred than # SHA1PRNG in the Sun provider. # securerandom.source=file:/dev/random # # A list of known strong SecureRandom implementations. # # To help guide applications in selecting a suitable strong # java.security.SecureRandom implementation, Java distributions should # indicate a list of known strong implementations using the property. # # This is a comma-separated list of algorithm and/or algorithm:provider # entries. # securerandom.strongAlgorithms=NativePRNGBlocking:SUN # # Class to instantiate as the javax.security.auth.login.Configuration # provider. # login.configuration.provider=sun.security.provider.ConfigFile # # Default login configuration file # #login.config.url.1=file:${user.home}/.java.login.config # # Class to instantiate as the system Policy. This is the name of the class # that will be used as the Policy object. # policy.provider=sun.security.provider.PolicyFile # The default is to have a single system-wide policy file, # and a policy file in the user's home directory. policy.url.1=file:${java.home}/lib/security/java.policy policy.url.2=file:${user.home}/.java.policy # whether or not we expand properties in the policy file # if this is set to false, properties (${...}) will not be expanded in policy # files. policy.expandProperties=true # whether or not we allow an extra policy to be passed on the command line # with -Djava.security.policy=somefile. Comment out this line to disable # this feature. policy.allowSystemProperty=true # whether or not we look into the IdentityScope for trusted Identities # when encountering a 1.1 signed JAR file. If the identity is found # and is trusted, we grant it AllPermission. policy.ignoreIdentityScope=false # # Default keystore type. # keystore.type=jks # # Controls compatibility mode for the JKS keystore type. # # When set to 'true', the JKS keystore type supports loading # keystore files in either JKS or PKCS12 format. When set to 'false' # it supports loading only JKS keystore files. # keystore.type.compat=true # # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageAccess unless the # corresponding RuntimePermission ("accessClassInPackage."+package) has # been granted. package.access=sun.,\ org.GNOME.Accessibility.,\ com.sun.xml.internal.,\ com.sun.imageio.,\ com.sun.istack.internal.,\ com.sun.jmx.,\ com.sun.media.sound.,\ com.sun.naming.internal.,\ com.sun.proxy.,\ com.sun.corba.se.,\ com.sun.org.apache.bcel.internal.,\ com.sun.org.apache.regexp.internal.,\ com.sun.org.apache.xerces.internal.,\ com.sun.org.apache.xpath.internal.,\ com.sun.org.apache.xalan.internal.extensions.,\ com.sun.org.apache.xalan.internal.lib.,\ com.sun.org.apache.xalan.internal.res.,\ com.sun.org.apache.xalan.internal.templates.,\ com.sun.org.apache.xalan.internal.utils.,\ com.sun.org.apache.xalan.internal.xslt.,\ com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ com.sun.org.apache.xalan.internal.xsltc.compiler.,\ com.sun.org.apache.xalan.internal.xsltc.trax.,\ com.sun.org.apache.xalan.internal.xsltc.util.,\ com.sun.org.apache.xml.internal.res.,\ com.sun.org.apache.xml.internal.resolver.helpers.,\ com.sun.org.apache.xml.internal.resolver.readers.,\ com.sun.org.apache.xml.internal.security.,\ com.sun.org.apache.xml.internal.serializer.utils.,\ com.sun.org.apache.xml.internal.utils.,\ com.sun.org.glassfish.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ oracle.jrockit.jfr.,\ org.jcp.xml.dsig.internal.,\ jdk.internal.,\ jdk.nashorn.internal.,\ jdk.nashorn.tools.,\ jdk.xml.internal.,\ com.sun.activation.registries. # # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageDefinition unless the # corresponding RuntimePermission ("defineClassInPackage."+package) has # been granted. # # by default, none of the class loaders supplied with the JDK call # checkPackageDefinition. # package.definition=sun.,\ com.sun.xml.internal.,\ com.sun.imageio.,\ com.sun.istack.internal.,\ com.sun.jmx.,\ com.sun.media.sound.,\ com.sun.naming.internal.,\ com.sun.proxy.,\ com.sun.corba.se.,\ com.sun.org.apache.bcel.internal.,\ com.sun.org.apache.regexp.internal.,\ com.sun.org.apache.xerces.internal.,\ com.sun.org.apache.xpath.internal.,\ com.sun.org.apache.xalan.internal.extensions.,\ com.sun.org.apache.xalan.internal.lib.,\ com.sun.org.apache.xalan.internal.res.,\ com.sun.org.apache.xalan.internal.templates.,\ com.sun.org.apache.xalan.internal.utils.,\ com.sun.org.apache.xalan.internal.xslt.,\ com.sun.org.apache.xalan.internal.xsltc.cmdline.,\ com.sun.org.apache.xalan.internal.xsltc.compiler.,\ com.sun.org.apache.xalan.internal.xsltc.trax.,\ com.sun.org.apache.xalan.internal.xsltc.util.,\ com.sun.org.apache.xml.internal.res.,\ com.sun.org.apache.xml.internal.resolver.helpers.,\ com.sun.org.apache.xml.internal.resolver.readers.,\ com.sun.org.apache.xml.internal.security.,\ com.sun.org.apache.xml.internal.serializer.utils.,\ com.sun.org.apache.xml.internal.utils.,\ com.sun.org.glassfish.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ oracle.jrockit.jfr.,\ org.jcp.xml.dsig.internal.,\ jdk.internal.,\ jdk.nashorn.internal.,\ jdk.nashorn.tools.,\ jdk.xml.internal.,\ com.sun.activation.registries. # # Determines whether this properties file can be appended to # or overridden on the command line via -Djava.security.properties # security.overridePropertiesFile=true # # Determines the default key and trust manager factory algorithms for # the javax.net.ssl package. # ssl.KeyManagerFactory.algorithm=BCFKS ssl.TrustManagerFactory.algorithm=PKIX # # The Java-level namelookup cache policy for successful lookups: # # any negative value: caching forever # any positive value: the number of seconds to cache an address for # zero: do not cache # # default value is forever (FOREVER). For security reasons, this # caching is made forever when a security manager is set. When a security # manager is not set, the default behavior in this implementation # is to cache for 30 seconds. # # NOTE: setting this to anything other than the default value can have # serious security implications. Do not set it unless # you are sure you are not exposed to DNS spoofing attack. # #networkaddress.cache.ttl=-1 # The Java-level namelookup cache policy for failed lookups: # # any negative value: cache forever # any positive value: the number of seconds to cache negative lookup results # zero: do not cache # # In some Microsoft Windows networking environments that employ # the WINS name service in addition to DNS, name service lookups # that fail may take a noticeably long time to return (approx. 5 seconds). # For this reason the default caching policy is to maintain these # results for 10 seconds. # # networkaddress.cache.negative.ttl=10 # # Properties to configure OCSP for certificate revocation checking # # Enable OCSP # # By default, OCSP is not used for certificate revocation checking. # This property enables the use of OCSP when set to the value "true". # # NOTE: SocketPermission is required to connect to an OCSP responder. # # Example, # ocsp.enable=true # # Location of the OCSP responder # # By default, the location of the OCSP responder is determined implicitly # from the certificate being validated. This property explicitly specifies # the location of the OCSP responder. The property is used when the # Authority Information Access extension (defined in RFC 3280) is absent # from the certificate or when it requires overriding. # # Example, # ocsp.responderURL=http://ocsp.example.net:80 # # Subject name of the OCSP responder's certificate # # By default, the certificate of the OCSP responder is that of the issuer # of the certificate being validated. This property identifies the certificate # of the OCSP responder when the default does not apply. Its value is a string # distinguished name (defined in RFC 2253) which identifies a certificate in # the set of certificates supplied during cert path validation. In cases where # the subject name alone is not sufficient to uniquely identify the certificate # then both the "ocsp.responderCertIssuerName" and # "ocsp.responderCertSerialNumber" properties must be used instead. When this # property is set then those two properties are ignored. # # Example, # ocsp.responderCertSubjectName="CN=OCSP Responder, O=XYZ Corp" # # Issuer name of the OCSP responder's certificate # # By default, the certificate of the OCSP responder is that of the issuer # of the certificate being validated. This property identifies the certificate # of the OCSP responder when the default does not apply. Its value is a string # distinguished name (defined in RFC 2253) which identifies a certificate in # the set of certificates supplied during cert path validation. When this # property is set then the "ocsp.responderCertSerialNumber" property must also # be set. When the "ocsp.responderCertSubjectName" property is set then this # property is ignored. # # Example, # ocsp.responderCertIssuerName="CN=Enterprise CA, O=XYZ Corp" # # Serial number of the OCSP responder's certificate # # By default, the certificate of the OCSP responder is that of the issuer # of the certificate being validated. This property identifies the certificate # of the OCSP responder when the default does not apply. Its value is a string # of hexadecimal digits (colon or space separators may be present) which # identifies a certificate in the set of certificates supplied during cert path # validation. When this property is set then the "ocsp.responderCertIssuerName" # property must also be set. When the "ocsp.responderCertSubjectName" property # is set then this property is ignored. # # Example, # ocsp.responderCertSerialNumber=2A:FF:00 # # Policy for failed Kerberos KDC lookups: # # When a KDC is unavailable (network error, service failure, etc), it is # put inside a blacklist and accessed less often for future requests. The # value (case-insensitive) for this policy can be: # # tryLast # KDCs in the blacklist are always tried after those not on the list. # # tryLess[:max_retries,timeout] # KDCs in the blacklist are still tried by their order in the configuration, # but with smaller max_retries and timeout values. max_retries and timeout # are optional numerical parameters (default 1 and 5000, which means once # and 5 seconds). Please notes that if any of the values defined here is # more than what is defined in krb5.conf, it will be ignored. # # Whenever a KDC is detected as available, it is removed from the blacklist. # The blacklist is reset when krb5.conf is reloaded. You can add # refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is # reloaded whenever a JAAS authentication is attempted. # # Example, # krb5.kdc.bad.policy = tryLast # krb5.kdc.bad.policy = tryLess:2,2000 krb5.kdc.bad.policy = tryLast # Algorithm restrictions for certification path (CertPath) processing # # In some environments, certain algorithms or key lengths may be undesirable # for certification path building and validation. For example, "MD2" is # generally no longer considered to be a secure hash algorithm. This section # describes the mechanism for disabling algorithms based on algorithm name # and/or key length. This includes algorithms used in certificates, as well # as revocation information such as CRLs and signed OCSP Responses. # The syntax of the disabled algorithm string is described as follows: # DisabledAlgorithms: # " DisabledAlgorithm { , DisabledAlgorithm } " # # DisabledAlgorithm: # AlgorithmName [Constraint] { '&' Constraint } # # AlgorithmName: # (see below) # # Constraint: # KeySizeConstraint | CAConstraint | DenyAfterConstraint | # UsageConstraint # # KeySizeConstraint: # keySize Operator KeyLength # # Operator: # <= | < | == | != | >= | > # # KeyLength: # Integer value of the algorithm's key length in bits # # CAConstraint: # jdkCA # # DenyAfterConstraint: # denyAfter YYYY-MM-DD # # UsageConstraint: # usage [TLSServer] [TLSClient] [SignedJAR] # # The "AlgorithmName" is the standard algorithm name of the disabled # algorithm. See "Java Cryptography Architecture Standard Algorithm Name # Documentation" for information about Standard Algorithm Names. Matching # is performed using a case-insensitive sub-element matching rule. (For # example, in "SHA1withECDSA" the sub-elements are "SHA1" for hashing and # "ECDSA" for signatures.) If the assertion "AlgorithmName" is a # sub-element of the certificate algorithm name, the algorithm will be # rejected during certification path building and validation. For example, # the assertion algorithm name "DSA" will disable all certificate algorithms # that rely on DSA, such as NONEwithDSA, SHA1withDSA. However, the assertion # will not disable algorithms related to "ECDSA". # # A "Constraint" defines restrictions on the keys and/or certificates for # a specified AlgorithmName: # # KeySizeConstraint: # keySize Operator KeyLength # The constraint requires a key of a valid size range if the # "AlgorithmName" is of a key algorithm. The "KeyLength" indicates # the key size specified in number of bits. For example, # "RSA keySize <= 1024" indicates that any RSA key with key size less # than or equal to 1024 bits should be disabled, and # "RSA keySize < 1024, RSA keySize > 2048" indicates that any RSA key # with key size less than 1024 or greater than 2048 should be disabled. # This constraint is only used on algorithms that have a key size. # # CAConstraint: # jdkCA # This constraint prohibits the specified algorithm only if the # algorithm is used in a certificate chain that terminates at a marked # trust anchor in the lib/security/cacerts keystore. If the jdkCA # constraint is not set, then all chains using the specified algorithm # are restricted. jdkCA may only be used once in a DisabledAlgorithm # expression. # Example: To apply this constraint to SHA-1 certificates, include # the following: "SHA1 jdkCA" # # DenyAfterConstraint: # denyAfter YYYY-MM-DD # This constraint prohibits a certificate with the specified algorithm # from being used after the date regardless of the certificate's # validity. JAR files that are signed and timestamped before the # constraint date with certificates containing the disabled algorithm # will not be restricted. The date is processed in the UTC timezone. # This constraint can only be used once in a DisabledAlgorithm # expression. # Example: To deny usage of RSA 2048 bit certificates after Feb 3 2020, # use the following: "RSA keySize == 2048 & denyAfter 2020-02-03" # # UsageConstraint: # usage [TLSServer] [TLSClient] [SignedJAR] # This constraint prohibits the specified algorithm for # a specified usage. This should be used when disabling an algorithm # for all usages is not practical. 'TLSServer' restricts the algorithm # in TLS server certificate chains when server authentication is # performed. 'TLSClient' restricts the algorithm in TLS client # certificate chains when client authentication is performed. # 'SignedJAR' constrains use of certificates in signed jar files. # The usage type follows the keyword and more than one usage type can # be specified with a whitespace delimiter. # Example: "SHA1 usage TLSServer TLSClient" # # When an algorithm must satisfy more than one constraint, it must be # delimited by an ampersand '&'. For example, to restrict certificates in a # chain that terminate at a distribution provided trust anchor and contain # RSA keys that are less than or equal to 1024 bits, add the following # constraint: "RSA keySize <= 1024 & jdkCA". # # All DisabledAlgorithms expressions are processed in the order defined in the # property. This requires lower keysize constraints to be specified # before larger keysize constraints of the same algorithm. For example: # "RSA keySize < 1024 & jdkCA, RSA keySize < 2048". # # Note: The algorithm restrictions do not apply to trust anchors or # self-signed certificates. # # Note: This property is currently used by Oracle's PKIX implementation. It # is not guaranteed to be examined and used by other implementations. # # Example: # jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048 # # jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, \ RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224 # # Algorithm restrictions for signed JAR files # # In some environments, certain algorithms or key lengths may be undesirable # for signed JAR validation. For example, "MD2" is generally no longer # considered to be a secure hash algorithm. This section describes the # mechanism for disabling algorithms based on algorithm name and/or key length. # JARs signed with any of the disabled algorithms or key sizes will be treated # as unsigned. # # The syntax of the disabled algorithm string is described as follows: # DisabledAlgorithms: # " DisabledAlgorithm { , DisabledAlgorithm } " # # DisabledAlgorithm: # AlgorithmName [Constraint] { '&' Constraint } # # AlgorithmName: # (see below) # # Constraint: # KeySizeConstraint | DenyAfterConstraint # # KeySizeConstraint: # keySize Operator KeyLength # # DenyAfterConstraint: # denyAfter YYYY-MM-DD # # Operator: # <= | < | == | != | >= | > # # KeyLength: # Integer value of the algorithm's key length in bits # # Note: This property is currently used by the JDK Reference # implementation. It is not guaranteed to be examined and used by other # implementations. # # See "jdk.certpath.disabledAlgorithms" for syntax descriptions. # jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, DSA keySize < 1024 # # Algorithm restrictions for Secure Socket Layer/Transport Layer Security # (SSL/TLS) processing # # In some environments, certain algorithms or key lengths may be undesirable # when using SSL/TLS. This section describes the mechanism for disabling # algorithms during SSL/TLS security parameters negotiation, including # protocol version negotiation, cipher suites selection, peer authentication # and key exchange mechanisms. # # Disabled algorithms will not be negotiated for SSL/TLS connections, even # if they are enabled explicitly in an application. # # For PKI-based peer authentication and key exchange mechanisms, this list # of disabled algorithms will also be checked during certification path # building and validation, including algorithms used in certificates, as # well as revocation information such as CRLs and signed OCSP Responses. # This is in addition to the jdk.certpath.disabledAlgorithms property above. # # See the specification of "jdk.certpath.disabledAlgorithms" for the # syntax of the disabled algorithm string. # # Note: The algorithm restrictions do not apply to trust anchors or # self-signed certificates. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # # Example: # jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \ EC keySize < 224, 3DES_EDE_CBC, anon, NULL # Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) # processing in JSSE implementation. # # In some environments, a certain algorithm may be undesirable but it # cannot be disabled because of its use in legacy applications. Legacy # algorithms may still be supported, but applications should not use them # as the security strength of legacy algorithms are usually not strong enough # in practice. # # During SSL/TLS security parameters negotiation, legacy algorithms will # not be negotiated unless there are no other candidates. # # The syntax of the legacy algorithms string is described as this Java # BNF-style: # LegacyAlgorithms: # " LegacyAlgorithm { , LegacyAlgorithm } " # # LegacyAlgorithm: # AlgorithmName (standard JSSE algorithm name) # # See the specification of security property "jdk.certpath.disabledAlgorithms" # for the syntax and description of the "AlgorithmName" notation. # # Per SSL/TLS specifications, cipher suites have the form: # SSL_KeyExchangeAlg_WITH_CipherAlg_MacAlg # or # TLS_KeyExchangeAlg_WITH_CipherAlg_MacAlg # # For example, the cipher suite TLS_RSA_WITH_AES_128_CBC_SHA uses RSA as the # key exchange algorithm, AES_128_CBC (128 bits AES cipher algorithm in CBC # mode) as the cipher (encryption) algorithm, and SHA-1 as the message digest # algorithm for HMAC. # # The LegacyAlgorithm can be one of the following standard algorithm names: # 1. JSSE cipher suite name, e.g., TLS_RSA_WITH_AES_128_CBC_SHA # 2. JSSE key exchange algorithm name, e.g., RSA # 3. JSSE cipher (encryption) algorithm name, e.g., AES_128_CBC # 4. JSSE message digest algorithm name, e.g., SHA # # See SSL/TLS specifications and "Java Cryptography Architecture Standard # Algorithm Name Documentation" for information about the algorithm names. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # There is no guarantee the property will continue to exist or be of the # same syntax in future releases. # # Example: # jdk.tls.legacyAlgorithms=DH_anon, DES_CBC, SSL_RSA_WITH_RC4_128_MD5 # jdk.tls.legacyAlgorithms= \ K_NULL, C_NULL, M_NULL, \ DH_anon, ECDH_anon, \ RC4_128, RC4_40, DES_CBC, DES40_CBC, \ 3DES_EDE_CBC # The pre-defined default finite field Diffie-Hellman ephemeral (DHE) # parameters for Transport Layer Security (SSL/TLS/DTLS) processing. # # In traditional SSL/TLS/DTLS connections where finite field DHE parameters # negotiation mechanism is not used, the server offers the client group # parameters, base generator g and prime modulus p, for DHE key exchange. # It is recommended to use dynamic group parameters. This property defines # a mechanism that allows you to specify custom group parameters. # # The syntax of this property string is described as this Java BNF-style: # DefaultDHEParameters: # DefinedDHEParameters { , DefinedDHEParameters } # # DefinedDHEParameters: # "{" DHEPrimeModulus , DHEBaseGenerator "}" # # DHEPrimeModulus: # HexadecimalDigits # # DHEBaseGenerator: # HexadecimalDigits # # HexadecimalDigits: # HexadecimalDigit { HexadecimalDigit } # # HexadecimalDigit: one of # 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f # # Whitespace characters are ignored. # # The "DefinedDHEParameters" defines the custom group parameters, prime # modulus p and base generator g, for a particular size of prime modulus p. # The "DHEPrimeModulus" defines the hexadecimal prime modulus p, and the # "DHEBaseGenerator" defines the hexadecimal base generator g of a group # parameter. It is recommended to use safe primes for the custom group # parameters. # # If this property is not defined or the value is empty, the underlying JSSE # provider's default group parameter is used for each connection. # # If the property value does not follow the grammar, or a particular group # parameter is not valid, the connection will fall back and use the # underlying JSSE provider's default group parameter. # # Note: This property is currently used by OpenJDK's JSSE implementation. It # is not guaranteed to be examined and used by other implementations. # # Example: # jdk.tls.server.defaultDHEParameters= # { \ # FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 \ # 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD \ # EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 \ # E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED \ # EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381 \ # FFFFFFFF FFFFFFFF, 2} # Cryptographic Jurisdiction Policy defaults # # Import and export control rules on cryptographic software vary from # country to country. By default, the JDK provides two different sets of # cryptographic policy files: # # unlimited: These policy files contain no restrictions on cryptographic # strengths or algorithms. # # limited: These policy files contain more restricted cryptographic # strengths, and are still available if your country or # usage requires the traditional restrictive policy. # # The JDK JCE framework uses the unlimited policy files by default. # However the user may explicitly choose a set either by defining the # "crypto.policy" Security property or by installing valid JCE policy # jar files into the traditional JDK installation location. To better # support older JDK Update releases, the "crypto.policy" property is not # defined by default. See below for more information. # # The following logic determines which policy files are used: # # refers to the directory where the JRE was # installed and may be determined using the "java.home" # System property. # # 1. If the Security property "crypto.policy" has been defined, # then the following mechanism is used: # # The policy files are stored as jar files in subdirectories of # /lib/security/policy. Each directory contains a complete # set of policy files. # # The "crypto.policy" Security property controls the directory # selection, and thus the effective cryptographic policy. # # The default set of directories is: # # limited | unlimited # # 2. If the "crypto.policy" property is not set and the traditional # US_export_policy.jar and local_policy.jar files # (e.g. limited/unlimited) are found in the legacy # /lib/security directory, then the rules embedded within # those jar files will be used. This helps preserve compatibility # for users upgrading from an older installation. # # 3. If the jar files are not present in the legacy location # and the "crypto.policy" Security property is not defined, # then the JDK will use the unlimited settings (equivalent to # crypto.policy=unlimited) # # Please see the JCA documentation for additional information on these # files and formats. # # YOU ARE ADVISED TO CONSULT YOUR EXPORT/IMPORT CONTROL COUNSEL OR ATTORNEY # TO DETERMINE THE EXACT REQUIREMENTS. # # Please note that the JCE for Java SE, including the JCE framework, # cryptographic policy files, and standard JCE providers provided with # the Java SE, have been reviewed and approved for export as mass market # encryption item by the US Bureau of Industry and Security. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # crypto.policy=unlimited # # The policy for the XML Signature secure validation mode. The mode is # enabled by setting the property "org.jcp.xml.dsig.secureValidation" to # true with the javax.xml.crypto.XMLCryptoContext.setProperty() method, # or by running the code with a SecurityManager. # # Policy: # Constraint {"," Constraint } # Constraint: # AlgConstraint | MaxTransformsConstraint | MaxReferencesConstraint | # ReferenceUriSchemeConstraint | KeySizeConstraint | OtherConstraint # AlgConstraint # "disallowAlg" Uri # MaxTransformsConstraint: # "maxTransforms" Integer # MaxReferencesConstraint: # "maxReferences" Integer # ReferenceUriSchemeConstraint: # "disallowReferenceUriSchemes" String { String } # KeySizeConstraint: # "minKeySize" KeyAlg Integer # OtherConstraint: # "noDuplicateIds" | "noRetrievalMethodLoops" # # For AlgConstraint, Uri is the algorithm URI String that is not allowed. # See the XML Signature Recommendation for more information on algorithm # URI Identifiers. For KeySizeConstraint, KeyAlg is the standard algorithm # name of the key type (ex: "RSA"). If the MaxTransformsConstraint, # MaxReferencesConstraint or KeySizeConstraint (for the same key type) is # specified more than once, only the last entry is enforced. # # Note: This property is currently used by the JDK Reference implementation. It # is not guaranteed to be examined and used by other implementations. # jdk.xml.dsig.secureValidationPolicy=\ disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\ disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\ maxTransforms 5,\ maxReferences 30,\ disallowReferenceUriSchemes file http https,\ minKeySize RSA 1024,\ minKeySize DSA 1024,\ minKeySize EC 224,\ noDuplicateIds,\ noRetrievalMethodLoops # # Serialization process-wide filter # # A filter, if configured, is used by java.io.ObjectInputStream during # deserialization to check the contents of the stream. # A filter is configured as a sequence of patterns, each pattern is either # matched against the name of a class in the stream or defines a limit. # Patterns are separated by ";" (semicolon). # Whitespace is significant and is considered part of the pattern. # # If the system property jdk.serialFilter is also specified, it supersedes # the security property value defined here. # # If a pattern includes a "=", it sets a limit. # If a limit appears more than once the last value is used. # Limits are checked before classes regardless of the order in the sequence of patterns. # If any of the limits are exceeded, the filter status is REJECTED. # # maxdepth=value - the maximum depth of a graph # maxrefs=value - the maximum number of internal references # maxbytes=value - the maximum number of bytes in the input stream # maxarray=value - the maximum array length allowed # # Other patterns, from left to right, match the class or package name as # returned from Class.getName. # If the class is an array type, the class or package to be matched is the element type. # Arrays of any number of dimensions are treated the same as the element type. # For example, a pattern of "!example.Foo", rejects creation of any instance or # array of example.Foo. # # If the pattern starts with "!", the status is REJECTED if the remaining pattern # is matched; otherwise the status is ALLOWED if the pattern matches. # If the pattern ends with ".**" it matches any class in the package and all subpackages. # If the pattern ends with ".*" it matches any class in the package. # If the pattern ends with "*", it matches any class with the pattern as a prefix. # If the pattern is equal to the class name, it matches. # Otherwise, the status is UNDECIDED. # # Primitive types are not configurable with this filter. # #jdk.serialFilter=pattern;pattern # # RMI Registry Serial Filter # # The filter pattern uses the same format as jdk.serialFilter. # This filter can override the builtin filter if additional types need to be # allowed or rejected from the RMI Registry or to decrease limits but not # to increase limits. # If the limits (maxdepth, maxrefs, or maxbytes) are exceeded, the object is rejected. # # The maxdepth of any array passed to the RMI Registry is set to # 10000. The maximum depth of the graph is set to 20. # These limits can be reduced via the maxarray, maxdepth limits. # #sun.rmi.registry.registryFilter=pattern;pattern # # Array construction of any component type, including subarrays and arrays of # primitives, are allowed unless the length is greater than the maxarray limit. # The filter is applied to each array element. # # The built-in filter allows subclasses of allowed classes and # can approximately be represented as the pattern: # #sun.rmi.registry.registryFilter=\ # maxarray=1000000;\ # maxdepth=20;\ # java.lang.String;\ # java.lang.Number;\ # java.lang.reflect.Proxy;\ # java.rmi.Remote;\ # sun.rmi.server.UnicastRef;\ # sun.rmi.server.RMIClientSocketFactory;\ # sun.rmi.server.RMIServerSocketFactory;\ # java.rmi.activation.ActivationID;\ # java.rmi.server.UID # # RMI Distributed Garbage Collector (DGC) Serial Filter # # The filter pattern uses the same format as jdk.serialFilter. # This filter can override the builtin filter if additional types need to be # allowed or rejected from the RMI DGC. # # The builtin DGC filter can approximately be represented as the filter pattern: # #sun.rmi.transport.dgcFilter=\ # java.rmi.server.ObjID;\ # java.rmi.server.UID;\ # java.rmi.dgc.VMID;\ # java.rmi.dgc.Lease;\ # maxdepth=5;maxarray=10000 # CORBA ORBIorTypeCheckRegistryFilter # Type check enhancement for ORB::string_to_object processing # # An IOR type check filter, if configured, is used by an ORB during # an ORB::string_to_object invocation to check the veracity of the type encoded # in the ior string. # # The filter pattern consists of a semi-colon separated list of class names. # The configured list contains the binary class names of the IDL interface types # corresponding to the IDL stub class to be instantiated. # As such, a filter specifies a list of IDL stub classes that will be # allowed by an ORB when an ORB::string_to_object is invoked. # It is used to specify a white list configuration of acceptable # IDL stub types which may be contained in a stringified IOR # parameter passed as input to an ORB::string_to_object method. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be examined and used by other implementations. # #com.sun.CORBA.ORBIorTypeCheckRegistryFilter=binary_class_name;binary_class_name # # JCEKS Encrypted Key Serial Filter # # This filter, if configured, is used by the JCEKS KeyStore during the # deserialization of the encrypted Key object stored inside a key entry. # If not configured or the filter result is UNDECIDED (i.e. none of the patterns # matches), the filter configured by jdk.serialFilter will be consulted. # # If the system property jceks.key.serialFilter is also specified, it supersedes # the security property value defined here. # # The filter pattern uses the same format as jdk.serialFilter. The default # pattern allows java.lang.Enum, java.security.KeyRep, java.security.KeyRep$Type, # and javax.crypto.spec.SecretKeySpec and rejects all the others. jceks.key.serialFilter = java.lang.Enum;java.security.KeyRep;\ java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;!* # # Policies for distrusting Certificate Authorities (CAs). # # This is a comma separated value of one or more case-sensitive strings, each # of which represents a policy for determining if a CA should be distrusted. # The supported values are: # # # SYMANTEC_TLS : Distrust TLS Server certificates anchored by a Symantec # root CA and issued after April 16, 2019 unless issued by one of the # following subordinate CAs which have a later distrust date: # 1. Apple IST CA 2 - G1, SHA-256 fingerprint: # AC2B922ECFD5E01711772FEA8ED372DE9D1E2245FCE3F57A9CDBEC77296A424B # Distrust after December 31, 2019. # 2. Apple IST CA 8 - G1, SHA-256 fingerprint: # A4FE7C7F15155F3F0AEF7AAA83CF6E06DEB97CA3F909DF920AC1490882D488ED # Distrust after December 31, 2019. # Leading and trailing whitespace surrounding each value are ignored. # Unknown values are ignored. If the property is commented out or set to the # empty String, no policies are enforced. # # Note: This property is currently used by the JDK Reference implementation. # It is not guaranteed to be supported by other SE implementations. Also, this # property does not override other security properties which can restrict # certificates such as jdk.tls.disabledAlgorithms or # jdk.certpath.disabledAlgorithms; those restrictions are still enforced even # if this property is not enabled. # jdk.security.caDistrustPolicies=SYMANTEC_TLS clj-http-client-1.2.0/dev-resources/logback-test.xml000066400000000000000000000006261373223535200224130ustar00rootroot00000000000000 %d %-5p [%t] [%c{2}] %m%n clj-http-client-1.2.0/dev-resources/logging.properties000066400000000000000000000001621373223535200230510ustar00rootroot00000000000000# register SLF4JBridgeHandler as handler for the j.u.l. root logger handlers = org.slf4j.bridge.SLF4JBridgeHandlerclj-http-client-1.2.0/dev-resources/ssl/000077500000000000000000000000001373223535200201075ustar00rootroot00000000000000clj-http-client-1.2.0/dev-resources/ssl/alternate-ca.pem000066400000000000000000000037101373223535200231530ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFkTCCA3mgAwIBAgIJAL1Ogx7eNBo3MA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV BAYTAlVTMQswCQYDVQQIDAJPUjERMA8GA1UEBwwIUG9ydGxhbmQxFDASBgNVBAoM C1B1cHBldCwgSW5jMRIwEAYDVQQDDAlhbHRlcm5hdGUwHhcNMTgwMzI4MjAzNzEz WhcNMjMwMzI3MjAzNzEzWjBXMQswCQYDVQQGEwJVUzELMAkGA1UECAwCT1IxETAP BgNVBAcMCFBvcnRsYW5kMRQwEgYDVQQKDAtQdXBwZXQsIEluYzESMBAGA1UEAwwJ YWx0ZXJuYXRlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwpLfuk4g /WpLxw2v6eCPs48kniZ4tuW+QtrZYoQVE3H2saf1j97oHvKw1DsJ48x9qmyTxKOF 4FDp4xAoOthEWbMnxydO8KREVDUbUiJTV2Dd6VTzyNxlCA7mtGVCNscgKZZwE83Q EnOVH6/q1dMcgHeXPzwSsTqh+4Ej8fgUmvuoK4Z2VGBGry5DQdSwdhNi+iFhtrBb BeaNGQ9G2oRiTPBh1sOqgO84HjPvHk8/lM6LZS41b+M/jEpSieU2LpwB9t+wVG34 TdS0UkL65yaPEX7Guv887Rvhe0uuk6FZnUP/8L0Wxy3M9yZyLV62GjGyfj9IMo6B R8sxprp5D9jMunHgHuZpE0wG4TpQ2xVt5sDkuCworaUlNhBBr+ibBC+q9k28Xyt1 c6A5Ck4Mvfuu+WFoPE0GcnmjoazL5lDjZot25ooVFOaHldFMV4qnPBskwJTZG64K VtBPV7vtI0BD49BdQIj4EVPXDOO0PgdzZShavS+YtldHrNmi1NT6aI4OWV7wwSvM KAHNqtWT4KcNN+q7VgfyyCBqmI+o82BIErmwEpWNlsMy8s9rRJEtnU+6p6SyNIj8 kkOHH4bla+Jg3FseXGq1T5WzZlHzU8I5/mZo8wXuoj1rRBEywdgGBP2QZncaFrLm 3DaQMbAGU5cHqdCA32wAtPRJmY9pPywr67UCAwEAAaNgMF4wHQYDVR0OBBYEFMgD SWTq5JTmQXKlhSr69e/u6xoYMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgGG MB8GA1UdIwQYMBaAFMgDSWTq5JTmQXKlhSr69e/u6xoYMA0GCSqGSIb3DQEBCwUA A4ICAQBl5O/yAWTxQW2Oede15AEfie6sYfAXFIRlWhh5OR0ZgThNDSiQQ/QcMRmT OZzuwjZ0VtH5wIDwFyU9a7yegB6AORwnZC7BzWWmu8o/JOPAv5cDDmvBOak29CUu Jy3ppdWMdxaoHTw33e+YqjaTZMKbpXtA5WmWZbGOKpfUQnNdqoiDEuPapcZByTiW 5mgzXR4WbYgBO0gtLu6b5K4Rq9IkU2b1H4CTpmhin7Rr8nYbrfnIx+rKg7fAa58a SozSuA6LmAmgUrrz6IxvqwONrCEiTpZBvZldiYiyBfnOw9C8HdUAhVgS2bdp78uy ARrbmcfNU/VIiKMP92dySNRxaQJPPYs+ufafQhItKEkyeljBbFpgMXZmnGLrwedc VTQyCEY6KDxc+29qfefE0aBh27GlJJ+Q2NodSHpjAefrQ/iDScYHjrHXXq22DKzP K6lLTH+ELLgeVw0vv4JYCowTQXG++Lazo1m27gzhXWU81SODZLe35d4pf+Wi5M/N JEoj9ZLzR124IpfCVGcvGeaIiFvRo5n94vHbA/HAspwLWgIfepGdtHA0yuku7hbT tM9zW3w2uwMa3I1kfpkpiOiS1xlOw+yScVcDZzn2hCvXK662wNInuYI6O+Lv3Een rZ//M9KhnsCLJb3d4DJMMm5RsXnUnApC6I64DilKORLaAY3j9w== -----END CERTIFICATE----- clj-http-client-1.2.0/dev-resources/ssl/ca.pem000066400000000000000000000037001373223535200211750ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFizCCA3OgAwIBAgIJAPpFWxUE/Xu5MA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV BAYTAlVTMQswCQYDVQQIDAJPUjERMA8GA1UEBwwIUG9ydGxhbmQxFDASBgNVBAoM C1B1cHBldCwgSW5jMQ8wDQYDVQQDDAZwdXBwZXQwHhcNMTgwMzI4MjAzNzA3WhcN MjMwMzI3MjAzNzA3WjBUMQswCQYDVQQGEwJVUzELMAkGA1UECAwCT1IxETAPBgNV BAcMCFBvcnRsYW5kMRQwEgYDVQQKDAtQdXBwZXQsIEluYzEPMA0GA1UEAwwGcHVw cGV0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeXxq5PH2Y8B6tC3 dSTJ91Cnp6Jc+jKtzSEbUAZzAt610wz66qByv+K9XZr6nMAnmnQktFe9TwcHjUq7 M7aDpAehLq7nziQTKGqz9fEkVftyBUo/GHkmsukVtPGR3kqbKu4J/YiIXRL1paBN rYOOJQ9PxbsKhPw8qsUe3bu0QOrpj+e0c+XVI7zau2EdWuI3QKYjVh+h6qo8dEe8 8Hn2R2fSKcU1M8OIBD4qcnuitFv7nCc+kzHRZfIje1iJaBT0ya7K/hbAr9Tc8xeu Wh5LC9fsu3Ar9fYuKDUKoQVf6DLYvQl0r7piK5N3kqYIVpCKgM3zpGS7OTfdG0E+ FBWTDY/SF397wWrWiyUwVI8j7YvQ3lRs7mDQH8TocNdgD3D/LJRYljtYTfG29m/B 8zit/aQRW3PzvxCfnGRQSy3hfx/T1zk+Oo6Y/02j3MXGNv+ODlz5V1YtWh+Bl6dF 1Y0TAEthqkx6xNZV3Re6r4ymrTnL2jApi/TPhJxxqZG2cCNnide5LKGUdZhOnklq JAO58dex3eKDByA6dfkKo5kzTvTPhq8d02LoaK8+npajGqR8setDv1UrkoF7hysz EB0669PYmkmorrGm31fcTnS9bZlhJysbCSmECcKdfCNlC9qU1tl2xq5QpHxHod2n BDCxfhahpkVn1rA6YoycEoNKQJkCAwEAAaNgMF4wHQYDVR0OBBYEFGzsV9cwJ1oY hEygrcaxbmCSFsrfMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgGGMB8GA1Ud IwQYMBaAFGzsV9cwJ1oYhEygrcaxbmCSFsrfMA0GCSqGSIb3DQEBCwUAA4ICAQCs l1sQspqkAMUu9juVBRSmK1qkfrop1cr3U3sVzcWvE177lyskQ2I2vcetjt+5HN8o YPami02qA5E9IUY2VxDfI1aceSNdldC6kaxdQhDFrd2C2hZVmn8niSH8ssSduN1d +JmYSSD2noQ+ZnqI+80xI/2lA6nDqIHezbR088znL2w2WLhaUHFuBxCXrt7hPvdU KjF+4viyOrz84RLDSsqBlapq8n4k9ZSklgK+U4bptTOsIl3E9YZf1sELLMCpJ9Ky BniZPj7ON4gOdGIR90iWCxYca1QLqmqFEXqEFgru7lgoXRYJ+OA046ruFI1ox13y LG9VEMMcb9jYtK7oZ5fVXmGzdfOw7x3LGyLK9GoPsLUuLdW06XSgvNu68g5HJDmb trDOPLIn2MjkaEHNgMcJyFkh4i7pOUFSOVF4IDJ/kGKBY1E0G7WfHuSJ/OfTvt0g uvr7xE0/GpEshpTvOTlVoEaf3UibDEUYQ6kjhXgG0UEl1UKY6qLtUb/mmnLQ+axm UhjKsZuORun5n5dHAS0uNpkHvIONM9ajRE4YWcqWSLGQSFk+3+hI0hVwMdvJY3GC UwRoSoLfwRPGBPlCdVVEm3ALnErerOOIFLRzffT4ljzZPoaUnizIcuH163L8yi0B N5XusBTCEP8DMJPVws4OnBLXK3xc7Sm8/LtKkDP0Hg== -----END CERTIFICATE----- clj-http-client-1.2.0/dev-resources/ssl/cert.pem000066400000000000000000000027351373223535200215560ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEJzCCAg8CCQD8aSTWNpzalTANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJV UzELMAkGA1UECAwCT1IxETAPBgNVBAcMCFBvcnRsYW5kMRQwEgYDVQQKDAtQdXBw ZXQsIEluYzEPMA0GA1UEAwwGcHVwcGV0MB4XDTE4MDMyODIwMzcwOFoXDTIzMDMy NzIwMzcwOFowVzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk9SMREwDwYDVQQHDAhQ b3J0bGFuZDEUMBIGA1UECgwLUHVwcGV0LCBJbmMxEjAQBgNVBAMMCWxvY2FsaG9z dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJlVv11OI5RGAH+Jn813 KpafOS6Fcbamy8TjAf/70JRL5rjHdGHrbMZXpe/ueRV/DiHosn+yxgqqgrl51eeq 8obEuTN+wamrz98HzWE7cxJjrtuoj++nWSo6Yux4hi3qJwFf+C+BwJBXQSfc2dFy 78r4dbLOILmQ1C8MG/OMrHdhwjyRL0ewrH93W5IYRA/+m6skcKeg5ifyLiqNfLmo uz4lV5sniN5WBA5FYF9uOxcp4499ZWh1aa8tE/j4QP6Lu7uv4xYb55Vmppr7uyXm ge9b1CF5j+po4nbtD6VMPu0A9/6wtjK1G1bvRsg/jRxmY+ZovK7OsiVbC0CTN7UF WXcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEADIUXJEXj7mTfriw96F4aeYxCywqX Nnn/IFKXzW2AKJz8bvFgUJNPMjBhdv+vZs8c1xD1ecIzwI2y66QSA2jePMOCVOfD qG67+Lcm57pChdPpi5UfLVZ8oKrIdhyhQF2vrXa4MmbcLiZXxe9DyAXUYrN8QFGL DFN7YyEe9rv6RYQuxptH4n8dLcvgKCPcJODjs/NhIHr+lto82wAGcd20/LOKe5iu K+CmwmDPh6qczNg4zfMUSxVKO9DTgI27oSlmTCV0hJroF5zGr3/puLs91gG0e4+l nCwEfO1vBKTggJ2IXHo99ho5cgf2uocPVLdZ6/141RvpjwMeHD+wnZSMRYOLEhQ3 Ehj6nN6OJkCY4IWov9/XYzzPc+aL30HkdfkKkB41Cj38dZVjQLn3OaxYcoiJgztd t/lqsvZR4+O8AiPVBEwtwj/4Bcj2rpbeQEKQJDqHDzndXcTPLYze69qIUzX5/KnW ymCuKn0/ZR/cn+QIjsciQgjylZlk4sQwmAn9i85XQoFHEvk6jSBFMv5g1JuPEEUp fFtjykpve12hPShrZBXvcBV1Hv3gIEzPqbZDKZ3j7RpNBQzAJTzumBR8dCLxBaun qqgkciLc33f6DMmdKmAaxKIixZfF6/dt1P0DKAMZCP1i7QniTAWqurxRe5gse1M8 aHhWU1capUmR8zk= -----END CERTIFICATE----- clj-http-client-1.2.0/dev-resources/ssl/key.pem000066400000000000000000000032131373223535200214010ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAmVW/XU4jlEYAf4mfzXcqlp85LoVxtqbLxOMB//vQlEvmuMd0 Yetsxlel7+55FX8OIeiyf7LGCqqCuXnV56ryhsS5M37BqavP3wfNYTtzEmOu26iP 76dZKjpi7HiGLeonAV/4L4HAkFdBJ9zZ0XLvyvh1ss4guZDULwwb84ysd2HCPJEv R7Csf3dbkhhED/6bqyRwp6DmJ/IuKo18uai7PiVXmyeI3lYEDkVgX247Fynjj31l aHVpry0T+PhA/ou7u6/jFhvnlWammvu7JeaB71vUIXmP6mjidu0PpUw+7QD3/rC2 MrUbVu9GyD+NHGZj5mi8rs6yJVsLQJM3tQVZdwIDAQABAoIBADkhS7WWAYejNVhP jKzfM4z/Miz1J1Mofk+TTRF1X1eBmCtMlT5Omy6jRX5J2e96cWeM7cTKdSNQOHim vl4cwVQZQ++90KZJD6BHpewOXGeDoIjTGx3tujkTm/wDQ+sjjPjkKvJ4Y6jj+bDC hp2VN2bmf+PpDSRWo9gLqPZreW7Y9/prMPkv5kScodDIlH+UxMZ6Ycg7uIWAIq5m 5Z0jt/+/vAe2aSLbIo7kCuVvzsaRKel0D4Y+C70m9Ts0Iz5Hq5uUAXNml4bOCa2W NFtySutOmRT6BVN6IOSH7ZQI78681KM7Zs2GYIt0dTHKxPeIO51OpUP7QSTDyTlS iIZH12ECgYEAyrYIuvlpDvdA9paAZWYmL5WFakCpI7bf4wg1iClRICFIb6DlsRAF rO7zdx9nSf2iT/+1WgHk0kGkf5e0o0BMZr27Zxo3CmU4NlnX2GhbhqGyqKmBF4l0 G/gthuX5+p+2wvEDnM9iycgkO1dNCU8fXnEaapPguPHyfSy7bGP4T58CgYEAwaTR 2WOjGuUs/wq1BeSl0oWO1bqNUT4sxjuCf+AyJRazebZAvmZ0ryrTPq4EwkYhJFxz ozTwD0TpMl+Hx2cIBOwtlzpQ0bmXFd1gAH9zDZzcz2xHnyzQaamJGYhhIoaRcUtS dBt+Sb0J0QV3C+cjuEzga7NiKsTwQspPa0YkxykCgYA2CHGpTSQI3jVNxW4yGRli o6ttWBD9uRhSjvSd981tEBIb1zqCiO/KWeNBxHgmy9nBEzsG4+xXczHi5NA8l74F wNs8L0uulK54JliXkbGb4Gwb8xq66GOvtOaFx7Vrbbksm0vIN6twd83Yl3usWI/W /7T35bUH904YuZy31qnwpwKBgF9Cpbk7utFQTIJq0gLEKI8YBNw/maWN4JjkTZ56 vDBiqogFkHQaEuzZNHJxSQYJWeR5VqnDMRVR4SZWuITvupIxXXw3w+poUlr/PGsC R6c6s6WWruUvNpfEPx3AaYiCiqohU76qh8XeqSWaeqXwXzIbH4vcAz7nAwrYW5l6 OD2BAoGATIqTh8LXs7nzzyLA84A4L3XPptipceTKTNHJJ3Yf70g6SW87+YAXvoIs abRXH7Km99UwzQPJZyF3IDvycxvEoBTTXUUKWHQvqOCwBvqPKTK1Wd2hj2BAiE4G B+8BHUsKQHbn8re2KfQ9rCKehdlVv3oDRHSi8sjAmv07hit4Acg= -----END RSA PRIVATE KEY----- clj-http-client-1.2.0/doc/000077500000000000000000000000001373223535200152655ustar00rootroot00000000000000clj-http-client-1.2.0/doc/clojure-client.md000066400000000000000000000214401373223535200205270ustar00rootroot00000000000000## Making requests with clojure clients clj-http-client allows you to make requests in two ways with clojure clients: with and without a persistent HTTP client. ## `create-client` clj-http-client allows you to create a persistent synchronous or asynchronous HTTP client using the `create-client` function from the corresponding namespace. The `create-client` function takes one argument, a map called `options`. The available options for configuring the client are detailed below. ### Base Options The following are the base set of options supported by the `create-client` functions. * `:force-redirects`: used to set whether or not the client should follow redirects on POST or PUT requests. Defaults to false. * `:follow-redirects`: used to set whether or not the client should follow redirects in general. Defaults to true. If set to false, will override the :force-redirects setting. * `:connect-timeout-milliseconds`: maximum number of milliseconds that the client will wait for a connection to be established. A value of 0 is interpreted as infinite. A negative value for or the absence of this option is interpreted as undefined (system default). * `:socket-timeout-milliseconds`: maximum number of milliseconds that the client will allow for no data to be available on the socket before closing the underlying connection, 'SO_TIMEOUT' in socket terms. A timeout of zero is interpreted as an infinite timeout. A negative value for or the absence of this setting is interpreted as undefined (system default). * `:ssl-protocols`: an array used to set the list of SSL protocols that the client could select from when talking to the server. Defaults to 'TLSv1', 'TLSv1.1', and 'TLSv1.2'. * `:cipher-suites`: an array used to set the cipher suites that the client could select from when talking to the server. Defaults to the complete set of suites supported by the underlying language runtime. * `:metric-registry`: a Dropwizard `MetricRegistry` to register http metrics to. If provided, metrics will automatically be registered for all requests made by the client. See the [metrics documentation](./metrics.md) for more info. * `:server-id`: a string for the name of the server the request is being made from. If specified, used in the namespace for metrics: `puppetlabs..http-client.experimental`. * `:metric-prefix`: a string for the prefix for metrics. If specified, metric namespace is `.http-client.experimental`. If both `metric-prefix` and `server-id` are specified, `metric-prefix` takes precendence. * `:max-connections-per-route`: an integer to specify the maximum number of concurrent requests for a given route (host & port) for a given persistant client instance. Defaults to 2. If 0 is specified, it acts as the default. * `:max-connections-total`: an integer to specify the maximum number of concurrent requests for a given persistant client instance. Defaults to 20. If 0 is specified, it acts as the default. ### SSL Options The following options are SSL specific, and only one of the following combinations is permitted. * `:ssl-context`: an instance of [SSLContext](http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLContext.html) OR * `:ssl-cert`: path to a PEM file containing the client cert * `:ssl-key`: path to a PEM file containing the client private key * `:ssl-ca-cert`: path to a PEM file containing the CA cert OR * `:ssl-ca-cert`: path to a PEM file containing the CA cert ### Making requests with a persistent client The `create-client` functions return a client with the following protocol: ```clj (defprotocol HTTPClient (get [this url] [this url opts]) (head [this url] [this url opts]) (post [this url] [this url opts]) (put [this url] [this url opts]) (delete [this url] [this url opts]) (trace [this url] [this url opts]) (options [this url] [this url opts]) (patch [this url] [this url opts]) (close [this])) ``` Each function will execute the corresponding HTTP request, with the exception of `close`, which will close the client. Each request function takes one argument, `url`, which is the URL against which you want to make your HTTP request. Each request function also has a two-arity version with an extra parameter, `options`, which is a map containing options for the HTTP request. These options are as follows: * `:headers`: optional; a map of headers. By default an 'accept-language' header with a value of `puppetlabs.core.i18n/user-locale` will be added to the request. * `:body`: optional; may be a String or any type supported by clojure's reader * `:compress-request-body`: optional; used to control any additional compression which the client can apply to the request body before it is sent to the target server. Defaults to `:none`. Supported values are: * `:gzip` which will compress the request body as gzip * `:none` which will not apply any additional compression to the request body * `:decompress-body`: optional; if `true`, an 'accept-encoding' header with a value of 'gzip, deflate' will be added to the request, and the response will be automatically decompressed if it contains a recognized 'content-encoding' header. Defaults to `true`. * `:as`: optional; used to control the data type of the response body. Defaults to `:stream`. Supported values are: * `:text` which will return a `String` * `:stream` which will return an `InputStream` * `:unbuffered-stream` which is a variant of `:stream` that will buffer as little data as possible * `:query-params`: optional; used to set the query parameters of an http request. This should be a map, where each key and each value is a String. * `:metric-id`: optional; a vector of keywords or strings. A metric will be created for each element in the vector, with each appending to the previous. For example, say you want to make a GET request with query parameter `abc` with value `def` to the URL `http://localhost:8080/test`. If you wanted to use a persistent synchronous client, you could make the request and print the body of the response like so: ```clj (let [client (sync/create-client {}) response (get client "http://localhost:8080/test" {:query-params {"abc" "def"}})] (println (:body response)) ``` If you wanted to use an asynchronous client, you could make the request and print the body of the response like so: ```clj (let [client (async/create-client {}) response (get client "http://localhost:8080/test" {:query-params {"abc" "def"}})] (println (:body @response))) ``` ### Closing a persistent client The `close` function takes no arguments. This function closes the client, and causes all resources associated with it to be cleaned up. This function must be called by the caller when they are done making requests with the client, as no implicit cleanup of the associated resources is done when the client is garbage collected. Once a client is closed, it can no longer be used to make any requests. ## Making a Request without a persistent client In addition to allowing you to create a persistent client with the `create-client` function, the puppetlabs.http.client.sync namespace provides the following simple request functions that can be called without a client: ```clj (get [url] [url opts]) (head [url] [url opts]) (post [url] [url opts]) (put [url] [url opts]) (delete [url] [url opts]) (trace [url] [url opts]) (options [url] [url opts]) (patch [url] [url opts]) (request [req]) ``` These functions will, for every request, create a new client, make a new request with that client, and then close the client once the response is received. Each of these functions (barring `request`) take one argument, `url`, which is the URL to which you want to make the request, and can optionally take a second argument, `options`. `options` is a map of options to configure both the client and the request, and as such takes the union of all options accepted by the `create-client` function and all options accepted by the request functions for a persistent client. For example, say you want to make a GET request to the URL `http://localhost:8080/test` with query parameter `abc` with value `def`, and you do not want redirects to be followed. In that case, you could do the following to make the request and print the body of the response: ```clj (let [response (get "http://localhost:8080/test" {:follow-redirects false :query-params {"abc" "def"}})] (println (:body response))) ``` A `request` function is also provided, which allows you to make a request of any type. `request` takes one argument, `req`, which is a map of options. It takes the same options as the simple request functions, but also takes the following required options: * `:url`: the URL against which to make the request. This should be a string. * `:method`: the HTTP method (:get, :head, :post, :put, :delete, :trace, :options, :patch) clj-http-client-1.2.0/doc/java-client.md000066400000000000000000000074301373223535200200100ustar00rootroot00000000000000## Making requests with the Java client Similarly to the way it is done in clojure code, clj-http-client allows you to make requests in two ways using Java: with and without a persistent client. ## `createClient(ClientOptions clientOptions)` clj-http-client allows you to create a persistent synchronous or asynchronous HTTP client using the static `createClient()` method in the [`Async`](../src/java/com/puppetlabs/http/client/Async.java) and [`Sync`](../src/java/com/puppetlabs/http/client/Sync.java) classes This method takes one argument, `clientOptions`, which is an instance of the [`ClientOptions`](../src/java/com/puppetlabs/http/client/ClientOptions.java) class, details on which can be found in its javadoc strings, linked above. ### Making requests with a persistent client The `createClient()` method returns an object implementing the [`SyncHttpClient`](../src/java/com/puppetlabs/http/client/SyncHttpClient.java) interface in the case of `Sync`, and the [`AsyncHttpClient`](../src/java/com/puppetlabs/http/client/AsyncHttpClient.java) interface in the case of `Async`. Information on the various methods available is detailed in the javadoc strings for the corresponding interfaces, which are linked above. The various request methods provided by these interfaces can take a [`RequestOptions`](../src/java/com/puppetlabs/http/client/RequestOptions.java) object, information on which can be found in that class' javadoc strings, linked above. For example, say you want to make a GET request against the URL `http://localhost:8080/test` with query parameter `abc` with value `def`. To make the request and print the body of the response with a persistent synchronous client, you could do the following: ```java ClientOptions options = new ClientOptions(); SyncHttpClient client = Sync.createClient(options); Response response = client.get(new URI("http://localhost:8080/test?abc=def")); System.out.println(response.getBody()); ``` If instead you wanted to use an asynchronous client, you could do the following: ```java ClientOptions options = new ClientOptions(); AsyncHttpClient client = Async.createClient(options); Promise response = client.get(new URI("http://localhost:8080/test?abc=def")); System.out.println(response.deref().getBody()); ``` ### Closing the client Each persistent client provides a `close` method, which can be used to close the client. This method will close the client and clean up all resources associated with it. It must be called by the caller when finished using the client to make requests, as there is no implicit cleanup of the associated resources when the client is garbage collected. Once the client is closed, it can no longer be used to make requests. ## Making a Request without a persistent client In addition to allowing you to create a persistent client with the `createClient()` method, the [`Sync`](../src/java/com/puppetlabs/http/client/Sync.java) class contains a number of simple request methods that allow for requests to be made without a persistent client. These are detailed in `Sync.java`'s javadoc strings, linked above. Many of the provided request methods take a [`SimpleRequestOptions`](../src/java/com/puppetlabs/http/client/SimpleRequestOptions.java) object. Information on this class can be found in its javadoc strings, linked above. As an example, say you wanted to make a request to the URL `http://localhost:8080/test` without a persistent client. You want the query parameter `abc` with value `def`, and you don't want redirects to be followed. In that case, you would do the following to print the body of the response: ```java SimpleRequestOptions options = new SimpleRequestOptions(new URI("http://localhost:8080/test?abc=def")); options = options.setFollowRedirects(false); Response response = Sync.get(options); System.out.println(response.getBody()); ```clj-http-client-1.2.0/doc/metrics.md000066400000000000000000000424421373223535200172630ustar00rootroot00000000000000# Metrics Both the Java client and the Clojure client have [Dropwizard Metrics](http://metrics.dropwizard.io/3.1.0/) support - they both accept as an option a `MetricRegistry` to which they will register http metrics for each request, as well as metrics for any metric-ids specified in the request options. This support is experimental - names of metrics and the exact API may change. For using metrics with either the Java client or the Clojure client you must already have created a Dropwizard `MetricRegistry`. - [Metric namespace](#metric-namespace) - [Types of metrics](#types-of-metrics) - [Getting back metrics](#getting-back-metrics) - [Clojure API](#clojure-api) - [Creating a client with metrics](#creating-a-client-with-metrics) - [Setting a metric-id](#setting-a-metric-id) - [Filtering metrics](#filtering-metrics) - [Filtering by URL](#filtering-by-url) - [Filtering by URL and method](#filtering-by-url-and-method) - [Filtering by metric-id](#filtering-by-metric-id) - [Java API](#java-api) - [Creating a client with metrics](#creating-a-client-with-metrics-1) - [Setting a metric-id](#setting-a-metric-id-1) - [Filtering metrics](#filtering-metrics-1) - [Filtering by URL](#filtering-by-url-1) - [Filtering by URL and method](#filtering-by-url-and-method-1) - [Filtering by metric-id](#filtering-by-metric-id-1) ## Metric namespace By default, http metrics are prefixed with the namespace `puppetlabs.http-client.experimental`. This namespace can be customized with two client options, `server-id` and `metric-prefix`. When `server-id` is set, the metric namespace becomes `puppetlabs..http-client.experimental`. When `metric-prefix` is set, the metric namespace becomes `.http-client.experimental`. If both `server-id` and `metric-prefix` are set, `metric-prefix` wins out and a warning message is logged. For a Clojure client, the `get-client-metric-namespace` protocol method can be used to get back the metric namespace set for the client. For a Java client, the `getMetricNamespace` method can be used to get back the configured metric namespace. ## Types of metrics There are two types of metrics: *full response* and *initial response*. Full response metrics stop when all bytes of the response have been read by the client. Initial response metrics stop when the first byte of the response has been received by the client. Full response metrics are suffixed with `full-response`. Initial response metric support has not yet been implementedare suffixed with `full-response`. Initial response metric support has not yet been implemented. There are three categories of metrics: `url` metrics, `url-and-method` metrics, and `metric-id` metrics. `url` and `url-and-method` metrics are created automatically for each request; `metric-id` metrics are only created for requests that have a `metric-id` request option specified. Each http request will have a metric name created for its url (stripped of query strings and url fragments), as well as for the url + method name. `url` metrics have `with-url` after the prefix, followed by the url. `url-and-method` metrics have `with-url-and-method` after the prefix, followed by the url and then the capitalized HTTP method. So, for example, a `GET` request to `http://foobar.com` would create a metric `puppetlabs.http-client.experimental.with-url.http://foobar.com.full-response` and a metric `puppetlabs.http-client.experimental.with-url-and-method.http://foobar.com.GET.full-response`. It is also possible to give a request a `metric-id`. The `metric-id` is an array of strings. For each element in the array, a metric name will be created, appending to the previous elements. `metric-id` metrics have `with-metric-id` after the metric prefix, followed by the metric-id. So, for example, for a metric-id `["foo", "bar", "baz"]`, the metrics `puppetlabs.http-client.experimental.with-metric-id.foo.full-response`, `puppetlabs.http-client.experimental.with-metric-id.foo.bar.full-response`, and `puppetlabs.http-client.experimental.with-metric-id.foo.bar.baz.full-response` would be created. ## Getting back metrics Both the Clojure API and the Java API have functions to get back from a `MetricRegistry` either the `Timer` objects or a selection of metric data, and to filter this information based on url, url and method, or metric-id. There are three different `Timer` objects: `UrlClientTimer` (which includes a field for the url and `getUrl` method), `UrlAndMethodClientTimer` (which includes a field for the url and a field for the method and accompanying `getUrl` and `getMethod` methods), and `MetricIdClientTimer` (which includes a field for the metric-id and accompanying `getMetricId` method). Each of these timers also has an `isCategory` method that returns whether the timer is or is not the provided `MetricCategory`. Both the Clojure and Java APIs also include functions that return a list of metrics data. This metrics data includes the `metric-name`, `count`, `mean` (in ms), and `aggregate` (computed as `count * mean`) for each metric. In addition, for `url` metrics the accompanying metrics data includes the `url`, for `url-and-method` metrics it includes the `url` and `method`, and for `metric-id` metrics it includes the `metric-id`. The Java API returns `ClientMetricData` objects - of which there are three types - `UrlClientMetricData`, `UrlAndMethodClientMetricData`, and `MetricIdClientMetricData`. The Clojure API returns maps with keys for this information. Both APIs have functions for returning all metrics/metric data, for returning all metrics/metrics data from a specific category, and for filtering metrics/metrics data within a category. ## Clojure API ### Creating a client with metrics To use metrics with the Clojure client, pass a `MetricRegistry` to it as part of the options map: ```clojure (async/create-client {:metric-registry (MetricRegistry.)}) ``` (the same works with the `sync` client). In [Trapperkeeper](https://github.com/puppetlabs/trapperkeeper) applications, creating and managing a `MetricRegistry` can be done easily with [trapperkeeper-metrics](https://github.com/puppetlabs/trapperkeeper-metrics): ```clojure (defservice my-trapperkeeper-service MyService [[:MetricsService get-metrics-registry]] (init [this context] (let [registry (get-metrics-registry) client (async/create-client {:metric-registry registry})] ...))) ``` Any client that is created with a `MetricRegistry` will automatically have `url` and `url-and-method` metrics registered. `get-client-metric-registry` protocol function can be called on a client to get the `MetricRegistry` from it. ### Setting a metric-id In addition to the `url` and `url-and-method` metrics, it is possible to set a `metric-id` for a request that will create additional metrics. For the Clojure API, a `metric-id` is a vector of keywords or strings. Either is supported by the API, however, if special characters are needed, use strings. Note than even when specified as a vector of keywords in the request option, the metric-id will be returned as a vector of strings in the metrics data. To set a `metric-id` for a request, include it as an option in the request options map. ```clojure (common/get client "https://foobar.com" {:metric-id [:foo :bar :baz]}) (common/get client "https://foobar.com" {:metric-id ["f/o/o"]}) ``` ### Filtering metrics To get all `Timer` objects registered for a `MetricRegistry`, use the `get-client-metrics` function in the `metrics` namespace. This takes the `MetricRegistry` as an argument and returns a map with three keys: `:url` `:url-and-method`, and `:metric-id`. Under each of these keys is a sequence of `Timer` objects of the corresponding type. If there are no timers of a certain type, the sequence will be empty. The output of this function conforms to the `common/AllTimers` schema. Example: ```clojure (common/get client "http://test.com" {:metric-id [:foo :bar :baz]}) ... (metrics/get-client-metrics metric-registry) => {:url [#object[com.puppetlabs.http.client.metrics.UrlClientTimer 0x66cf1f05 "com.puppetlabs.http.client.metrics.UrlClientTimer@66cf1f05"]] :url-and-method [#object[com.puppetlabs.http.client.metrics.UrlAndMethodClientTimer 0x6fe5444c "com.puppetlabs.http.client.metrics.UrlAndMethodClientTimer@6fe5444c"]] :metric-id [#object[com.puppetlabs.http.client.metrics.MetricIdClientTimer 0x690c10c5 "com.puppetlabs.http.client.metrics.MetricIdClientTimer@690c10c5"] #object[com.puppetlabs.http.client.metrics.MetricIdClientTimer 0xb7aca2e "com.puppetlabs.http.client.metrics.MetricIdClientTimer@b7aca2e"] #object[com.puppetlabs.http.client.metrics.MetricIdClientTimer 0x4ef82829 "com.puppetlabs.http.client.metrics.MetricIdClientTimer@4ef82829"]]} ``` To get metric data for all `Timer`s registered on a `MetricRegistry`, use the `get-client-metrics-data` function. This takes the `MetricRegistry` and returns a map with `:url`, `:url-and-method`, and `:metric-id` as keys. Under each of these keys is a sequence of maps, each map containing metrics data (see [Getting back metrics](#Getting-back-metrics) above, conforming to the `common/AllMetricsData` schema. Example: ```clojure (common/get client "http://test.com" {:metric-id [:foo :bar :baz]}) ... (metrics/get-client-metrics-data metric-registry) => {:url ({:count 1 :mean 553 :aggregate 553 :metric-name "puppetlabs.http-client.experimental.with-url.http://test.com.full-response" :url "http://test.com"}) :url-and-method ({:count 1 :mean 554 :aggregate 554 :metric-name "puppetlabs.http-client.experimental.with-url-and-method.http://test.com.GET.full-response" :url "http://test.com" :method "GET"}) :metric-id ({:count 1 :mean 554 :aggregate 554 :metric-name "puppetlabs.http-client.experimental.with-metric-id.foo.bar.baz.full-response" :metric-id ["foo" "bar" "baz"]} {:count 1 :mean 554 :aggregate 554 :metric-name "puppetlabs.http-client.experimental.with-metric-id.foo.bar.full-response" :metric-id ["foo" "bar"]} {:count 1 :mean 554 :aggregate 554 :metric-name "puppetlabs.http-client.experimental.with-metric-id.foo.full-response" :metric-id ["foo"]})} ``` #### Filtering by URL To get URL metrics and metrics data, use the `get-client-metrics-by-url` function and `get-client-metrics-data-by-url` in the `metrics` namespace. Both of these take as arguments the `MetricRegistry` and a string url. If no url is provided, return all url metrics/metrics data in a sequence. If no metrics are registered for that url, return an empty sequence. Example: ```clojure (common/get client "http://test.com" {:metric-id [:foo :bar :baz]}) ... (metrics/get-client-metrics-data-by-url metric-registry "http://test.com") => ({:count 1 :mean 553 :aggregate 553 :metric-name "puppetlabs.http-client.experimental.with-url.http://test.com.full-response" :url "http://test.com"}) (metrics/get-client-metrics-data-by-url metric-registry "http://not-a-matching-url.com") => () ``` #### Filtering by URL and method To get URL and method metrics and metrics data, use the `get-client-metrics-by-url-and-method` and `get-client-metrics-data-by-url-and-method` functions in the `metrics` namespace. Both of these take as arguments the `MetricRegistry`, a string url, and a keyword HTTP method. If no url and method is provided, return all url metrics/metrics data in a sequence. If no metrics are registered for that url and method, return an empty sequence. Example: ```clojure (common/get client "http://test.com" {:metric-id [:foo :bar :baz]}) ... (metrics/get-client-metrics-data-by-url-and-method metric-registry "http://test.com" :get) => ({:count 1 :mean 554 :aggregate 554 :metric-name "puppetlabs.http-client.experimental.with-url-and-method.http://test.com.GET.full-response" :url "http://test.com" :method "GET"}) :method "GET"}) (metrics/get-client-metrics-data-by-url-and-method metric-registry "http://test.com" :post) => () ``` #### Filtering by metric-id To get metric-id metrics and metrics data, use the `get-client-metrics-by-metric-id` and `get-client-metrics-data-by-metric-id` functions in the `metrics` namespace. Both of these take as arguments the `MetricRegistry` and a metric-id - as a vector of keywords or strings (if special characters are needed, use strings). If no metric-id is provided, will return all metric-id metrics. If no metrics are registered for that metric-id, returns an empty list. Example: ```clojure (common/get client "http://test.com" {:metric-id [:foo :bar :baz]}) ... (metrics/get-client-metrics-data-by-metric-id metric-registry [:foo]) => ({:count 1 :mean 554 :aggregate 554 :metric-name "puppetlabs.http-client.experimental.with-metric-id.foo.full-response" :metric-id ["foo"]}) (metrics/get-client-metrics-data-by-metric-id metric-registry [:foo :bar]) => ({:count 1 :mean 554 :aggregate 554 :metric-name "puppetlabs.http-client.experimental.with-metric-id.foo.bar.full-response" :metric-id ["foo" "bar"]}) (metrics/get-client-metrics-data-by-metric-id metric-registry [:foo :nope]) => () ``` ## Java API ### Creating a client with metrics To use metrics with the Java client, call `setMetricRegistry()` on the `ClientOptions` before supplying them to create the client. ```java MetricRegistry registry = new MetricRegistry(); ClientOptions options = new ClientOptions(); AsyncHttpClient client = Async.createClient(options); ``` (the same works with the `SyncHttpClient`). Any client that is created with a `MetricRegistry` will automatically have `url` and `url-and-method` metrics registered. `getMetricRegistry()` can be called on a client to get the `MetricRegistry` from it. ### Setting a metric-id In addition to the `url` and `url-and-method` metrics, it is possible to set a `metric-id` for a request that will create additional metrics. A `metric-id` is an array of strings. To set a `metric-id` for a request, use the `.setMetricId` method on the `RequestOptions` class. ```java RequestOptions options = new RequestOptions("https://foobar.com"); options.setMetricId(["foo", "bar", "baz"]); client.get(options); ``` `getMetricId()` can be called on `RequestOptions` to get back the `metric-id`. ### Filtering metrics To get all `Timer` objects registered for a `MetricRegistry`, use the `getClientMetrics()` method in the `Metrics` class. This takes the `MetricRegistry` as an argument and returns a `ClientTimerContainer` object. The `ClientTimerContainer` object has three fields - `urlTimers`, `urlAndMethodTimers`, and `metricIdTimers`. The list of `URLClientTimers` can be retrieved from the `ClientTimerContainer` with the `getUrlTimers()` method. A list of `URlAndMethodClientTimers` can be retrieved with the `getUrlAndMethod()` method. A list of `MetricIdClientTimer`s can be retrieved with the `getMetricIdTimers()` method. To get all `MetricData` objects, representing data for each `Metric` registered for a `MetricRegistry`, use the `getClientMetricsData()` methods in the `Metric` class. This takes the `MetricRegistry` as an argument and returns a `ClientMetricDataContainer` object. This object has a field for each type of metric data - `urlData`, `urlAndMethodData`, and `metricIdData`. Each field has an associated getter returning a list of the appropriate data object - `UrlClientMetricData`, `UrlAndMethodClientMetricData`, and `MetricIdClientMetricData`. #### Filtering by URL To get URL metrics and metrics data, use the `getClientMetricsByUrl()` and `getClientMetricsDataByUrl()` methods in the `Metric` class. Both of these take as arguments the `MetricRegistry` and a string url. If no url is provided, return all url metrics. If no metrics are registered for that url, return an empty list. `getClientMetricsByUrl()` returns a list of `UrlClientTimer` objects. `getClientMetricsDataByUrl` returns a list of `UrlClientMetricData` objects. #### Filtering by URL and method To get URL and method metrics and metrics data, use the `getClientMetricsByUrlAndMethod()` and `getClientMetricsDataByUrlAndMethod()` methods in the `Metric` class. Both of these take as arguments the `MetricRegistry`, a string url, and a string HTTP method. If no url or method is provided, will return all url-and-method metrics. If no metrics are registered for that url and method, returns an empty list. `getClientMetricsByUrlAndMethod()` returns a list of `UrlAndMethodClientTimer` objects. `getClientMetricsDataByUrlAndMethod` returns a list of `UrlAndMethodClientMetricData` objects. #### Filtering by metric-id To get metric-id metrics and metrics data, use the `getClientMetricsByMetricId()` and `getClientMetricsDataByMetricId()` methods in the `Metric` class. Both of these take as arguments the `MetricRegistry` and a metric-id - as an array of strings. If no metric-id is provided, will return all metric-id metrics. If no metrics are registered for that metric-id, returns an empty list. `getClientMetricsByMetricId()` returns a list of `MetricIdClientTimer` objects. `getClientMetricsDataByMetricId()` returns a list of `MetricIdClientMetricData` objects. clj-http-client-1.2.0/jenkins/000077500000000000000000000000001373223535200161615ustar00rootroot00000000000000clj-http-client-1.2.0/jenkins/deploy.sh000077500000000000000000000003361373223535200200160ustar00rootroot00000000000000#!/usr/bin/env bash set -e set -x git fetch --tags lein test echo "Tests passed!" lein release echo "Release plugin successful, pushing changes to git" git push origin --tags HEAD:$BRANCH echo "git push successful." clj-http-client-1.2.0/locales/000077500000000000000000000000001373223535200161425ustar00rootroot00000000000000clj-http-client-1.2.0/locales/eo.po000066400000000000000000000013151373223535200171050ustar00rootroot00000000000000# Esperanto translations for puppetlabs.http_client package. # Copyright (C) 2017 Puppet # This file is distributed under the same license as the puppetlabs.http_client package. # Automatically generated, 2017. # msgid "" msgstr "" "Project-Id-Version: puppetlabs.http_client \n" "Report-Msgid-Bugs-To: docs@puppet.com\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: src/clj/puppetlabs/http/client/async.clj msgid "Unsupported request method: {0}" msgstr "" clj-http-client-1.2.0/locales/http-client.pot000066400000000000000000000012561373223535200211250ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Puppet # This file is distributed under the same license as the puppetlabs.http_client package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: puppetlabs.http_client \n" "Report-Msgid-Bugs-To: docs@puppet.com\n" "POT-Creation-Date: \n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/clj/puppetlabs/http/client/async.clj msgid "Unsupported request method: {0}" msgstr "" clj-http-client-1.2.0/project.clj000066400000000000000000000104031373223535200166560ustar00rootroot00000000000000(defproject puppetlabs/http-client "1.2.0" :description "HTTP client wrapper" :license {:name "Apache License, Version 2.0" :url "http://www.apache.org/licenses/LICENSE-2.0.html"} :min-lein-version "2.9.1" :parent-project {:coords [puppetlabs/clj-parent "4.2.10"] :inherit [:managed-dependencies]} ;; Abort when version ranges or version conflicts are detected in ;; dependencies. Also supports :warn to simply emit warnings. ;; requires lein 2.2.0+. :pedantic? :abort :dependencies [[org.clojure/clojure] [org.apache.httpcomponents/httpasyncclient] [prismatic/schema] [commons-io] [io.dropwizard.metrics/metrics-core] [puppetlabs/ssl-utils] [puppetlabs/i18n] [org.slf4j/jul-to-slf4j]] :source-paths ["src/clj"] :java-source-paths ["src/java"] :jar-exclusions [#".*\.java$"] ;; By declaring a classifier here and a corresponding profile below we'll get an additional jar ;; during `lein jar` that has all the source code (including the java source). Downstream projects can then ;; depend on this source jar using a :classifier in their :dependencies. :classifiers [["sources" :sources-jar]] :profiles {:defaults {:dependencies [[cheshire] [puppetlabs/kitchensink :classifier "test"] [puppetlabs/trapperkeeper] [puppetlabs/trapperkeeper :classifier "test"] [puppetlabs/trapperkeeper-webserver-jetty9] [puppetlabs/trapperkeeper-webserver-jetty9 :classifier "test"] [puppetlabs/ring-middleware]] :resource-paths ["dev-resources"] :jvm-opts ["-Djava.util.logging.config.file=dev-resources/logging.properties"]} :dev [:defaults {:dependencies [[org.bouncycastle/bcpkix-jdk15on]]}] :fips [:defaults {:dependencies [[org.bouncycastle/bcpkix-fips] [org.bouncycastle/bc-fips] [org.bouncycastle/bctls-fips]] ;; this only ensures that we run with the proper profiles ;; during testing. This JVM opt will be set in the puppet module ;; that sets up the JVM classpaths during installation. :jvm-opts ~(let [version (System/getProperty "java.version") [major minor _] (clojure.string/split version #"\.") unsupported-ex (ex-info "Unsupported major Java version. Expects 8 or 11." {:major major :minor minor})] (condp = (java.lang.Integer/parseInt major) 1 (if (= 8 (java.lang.Integer/parseInt minor)) ["-Djava.security.properties==dev-resources/jdk8-fips-security"] (throw unsupported-ex)) 11 ["-Djava.security.properties==dev-resources/jdk11-fips-security"] (throw unsupported-ex)))}] :sources-jar {:java-source-paths ^:replace [] :jar-exclusions ^:replace [] :source-paths ^:replace ["src/clj" "src/java"]}} :deploy-repositories [["releases" {:url "https://clojars.org/repo" :username :env/clojars_jenkins_username :password :env/clojars_jenkins_password :sign-releases false}]] :lein-release {:scm :git :deploy-via :lein-deploy} :plugins [[lein-parent "0.3.7"] [puppetlabs/i18n "0.8.0"]] :repositories [["puppet-releases" "https://artifactory.delivery.puppetlabs.net/artifactory/list/clojure-releases__local/"] ["puppet-snapshots" "https://artifactory.delivery.puppetlabs.net/artifactory/list/clojure-snapshots__local/"]]) clj-http-client-1.2.0/src/000077500000000000000000000000001373223535200153075ustar00rootroot00000000000000clj-http-client-1.2.0/src/clj/000077500000000000000000000000001373223535200160575ustar00rootroot00000000000000clj-http-client-1.2.0/src/clj/puppetlabs/000077500000000000000000000000001373223535200202365ustar00rootroot00000000000000clj-http-client-1.2.0/src/clj/puppetlabs/http/000077500000000000000000000000001373223535200212155ustar00rootroot00000000000000clj-http-client-1.2.0/src/clj/puppetlabs/http/client/000077500000000000000000000000001373223535200224735ustar00rootroot00000000000000clj-http-client-1.2.0/src/clj/puppetlabs/http/client/async.clj000066400000000000000000000336341373223535200243130ustar00rootroot00000000000000;; This namespace is a wrapper around the http client functionality provided ;; by Apache HttpAsyncClient. It allows the use of PEM files for HTTPS configuration. ;; ;; In the options to any request method, an existing SSLContext object can be ;; supplied under :ssl-context. If this is present it will be used. If it's ;; not, the wrapper will attempt to use a set of PEM files stored in ;; (:ssl-cert :ssl-key :ssl-ca-cert) to create the SSLContext. ;; ;; See the puppetlabs.http.sync namespace for synchronous versions of all ;; these methods. (ns puppetlabs.http.client.async (:import (com.puppetlabs.http.client ClientOptions RequestOptions ResponseBodyType HttpMethod CompressType) (com.puppetlabs.http.client.impl JavaClient ResponseDeliveryDelegate) (org.apache.http.impl.nio.client CloseableHttpAsyncClient) (org.apache.http.client.utils URIBuilder) (org.apache.http.entity ContentType) (org.apache.http.nio.client HttpAsyncClient) (com.codahale.metrics MetricRegistry) (java.util Locale) (java.net URI URL)) (:require [puppetlabs.http.client.common :as common] [schema.core :as schema] [puppetlabs.http.client.metrics :as metrics] [puppetlabs.i18n.core :as i18n :refer [trs]] [clojure.string :as str]) (:refer-clojure :exclude (get))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Private utility functions (schema/defn ^:always-validate create-default-client :- CloseableHttpAsyncClient [{:keys [ssl-context ssl-ca-cert ssl-cert ssl-key ssl-protocols cipher-suites follow-redirects force-redirects connect-timeout-milliseconds socket-timeout-milliseconds metric-registry server-id metric-prefix enable-url-metrics? max-connections-total max-connections-per-route]}:- common/ClientOptions] (let [client-options (ClientOptions.)] (cond-> client-options (some? ssl-context) (.setSslContext ssl-context) (some? ssl-cert) (.setSslCert ssl-cert) (some? ssl-ca-cert) (.setSslCaCert ssl-ca-cert) (some? ssl-key) (.setSslKey ssl-key) (some? ssl-protocols) (.setSslProtocols (into-array String ssl-protocols)) (some? cipher-suites) (.setSslCipherSuites (into-array String cipher-suites)) (some? force-redirects) (.setForceRedirects force-redirects) (some? follow-redirects) (.setFollowRedirects follow-redirects) (some? connect-timeout-milliseconds) (.setConnectTimeoutMilliseconds connect-timeout-milliseconds) (some? socket-timeout-milliseconds) (.setSocketTimeoutMilliseconds socket-timeout-milliseconds) (some? metric-registry) (.setMetricRegistry metric-registry) (some? server-id) (.setServerId server-id) (some? metric-prefix) (.setMetricPrefix metric-prefix) (some? enable-url-metrics?) (.setEnableURLMetrics enable-url-metrics?) (some? max-connections-total) (.setMaxConnectionsTotal max-connections-total) (some? max-connections-per-route) (.setMaxConnectionsPerRoute max-connections-per-route)) (JavaClient/createClient client-options))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Map the Ring request onto the Java API (schema/defn callback-response :- common/Response [opts :- common/UserRequestOptions callback :- common/ResponseCallbackFn response :- common/Response] (if callback (try (callback response) (catch Exception e {:opts opts :error e})) response)) (schema/defn java-content-type->clj :- common/ContentType [java-content-type :- (schema/maybe ContentType)] (if java-content-type {:mime-type (.getMimeType java-content-type) :charset (.getCharset java-content-type)})) (schema/defn get-response-delivery-delegate :- ResponseDeliveryDelegate [opts :- common/UserRequestOptions result :- common/ResponsePromise] (reify ResponseDeliveryDelegate (deliverResponse [_ _ orig-encoding body headers status-code reason-phrase content-type callback] (->> {:opts opts :orig-content-encoding orig-encoding :status status-code :reason-phrase reason-phrase :headers (into {} headers) :content-type (java-content-type->clj content-type) :body body} (callback-response opts callback) (deliver result))) (deliverResponse [_ _ e callback] (->> {:opts opts :error e} (callback-response opts callback) (deliver result))))) (schema/defn clojure-method->java [opts :- common/UserRequestOptions] (case (:method opts) :delete HttpMethod/DELETE :get HttpMethod/GET :head HttpMethod/HEAD :options HttpMethod/OPTIONS :patch HttpMethod/PATCH :post HttpMethod/POST :put HttpMethod/PUT :trace HttpMethod/TRACE (throw (IllegalArgumentException. ^String (trs "Unsupported request method: {0}" (:method opts)))))) (schema/defn url-uri-string->uri :- URI [thing :- common/UrlOrUriOrString] (cond (= (type thing) URL) (.toURI thing) (= (type thing) String) (-> (URIBuilder. ^String thing) (.build)) :default thing)) (schema/defn parse-url :- URI [{:keys [url query-params]}] (let [uri (url-uri-string->uri url)] (if (nil? query-params) uri (let [uri-builder (reduce #(.addParameter %1 (key %2) (val %2)) (.clearParameters (URIBuilder. ^URI uri)) query-params)] (.build uri-builder))))) (schema/defn clojure-response-body-type->java :- ResponseBodyType [opts :- common/RequestOptions] (case (:as opts) :unbuffered-stream ResponseBodyType/UNBUFFERED_STREAM :text ResponseBodyType/TEXT ResponseBodyType/STREAM)) (schema/defn clojure-compress-request-body-type->java :- CompressType [opts :- common/RequestOptions] (case (:compress-request-body opts) :gzip CompressType/GZIP CompressType/NONE)) (defn parse-metric-id [opts] (when-let [metric-id (:metric-id opts)] (into-array (map name metric-id)))) (schema/defn clojure-options->java :- RequestOptions [opts :- common/RequestOptions] (-> ^URI (parse-url opts) RequestOptions. (.setAs (clojure-response-body-type->java opts)) (.setBody (:body opts)) (.setDecompressBody (clojure.core/get opts :decompress-body true)) (.setCompressRequestBody (clojure-compress-request-body-type->java opts)) (.setHeaders (:headers opts)) (.setMetricId (parse-metric-id opts)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Public (schema/defn ^:always-validate request-with-client :- common/ResponsePromise "Issues an async HTTP request with the specified client and returns a promise object to which the value of `(callback {:opts _ :status _ :headers _ :body _})` or `(callback {:opts _ :error _})` will be delivered. When unspecified, `callback` is the identity function. opts: * :url * :method - the HTTP method (:get, :head, :post, :put, :delete, :trace, :options, :patch) * :headers - a map of headers * :body - the body; may be a String or any type supported by clojure's reader * :decompress-body - if `true`, an 'accept-encoding' header with a value of 'gzip, deflate' will be added to the request, and the response will be automatically decompressed if it contains a recognized 'content-encoding' header. defaults to `true`. * :as - used to control the data type of the response body. Supported values are `:text`, `:stream` and `:unbuffered-stream`. `:text` will return a `String`, `:stream` and `:unbuffered-stream` will return an `InputStream`. Note that `:stream` holds the full response in memory (i.e. a `ByteArrayInputStream`). Use `:unbufferred-stream` for large response bodies or to consume less memory. Defaults to `:stream`. * :query-params - used to set the query parameters of an http request * :metric-id - array of strings or keywords, used to set the metrics to be timed for the request." ([opts :- common/RawUserRequestOptions callback :- common/ResponseCallbackFn client :- HttpAsyncClient] (request-with-client opts callback client nil nil true)) ([opts :- common/RawUserRequestOptions callback :- common/ResponseCallbackFn client :- HttpAsyncClient metric-registry :- (schema/maybe MetricRegistry) metric-namespace :- (schema/maybe schema/Str)] (request-with-client opts callback client metric-registry metric-namespace true)) ([opts :- common/RawUserRequestOptions callback :- common/ResponseCallbackFn client :- HttpAsyncClient metric-registry :- (schema/maybe MetricRegistry) metric-namespace :- (schema/maybe schema/Str) enable-url-metrics? :- schema/Bool] (let [result (promise) defaults {:body nil :decompress-body true :compress-request-body :none :as :stream} ^Locale locale (i18n/user-locale) ;; lower-case the header names so that we don't end up with ;; Accept-Language *AND* accept-language in the headers headers (into {"accept-language" (.toLanguageTag locale)} (for [[header value] (:headers opts)] [(str/lower-case header) value])) opts (-> (merge defaults opts) (assoc :headers headers)) java-request-options (clojure-options->java opts) java-method (clojure-method->java opts) response-delivery-delegate (get-response-delivery-delegate opts result)] (JavaClient/requestWithClient java-request-options java-method callback client response-delivery-delegate metric-registry metric-namespace enable-url-metrics?) result))) (schema/defn create-client :- (schema/protocol common/HTTPClient) "Creates a client to be used for making one or more HTTP requests. opts (base set): * :force-redirects - used to set whether or not the client should follow redirects on POST or PUT requests. Defaults to false. * :follow-redirects - used to set whether or not the client should follow redirects in general. Defaults to true. If set to false, will override the :force-redirects setting. * :connect-timeout-milliseconds - maximum number of milliseconds that the client will wait for a connection to be established. A value of zero is interpreted as infinite. A negative value for or the absence of this option is interpreted as undefined (system default). * :socket-timeout-milliseconds - maximum number of milliseconds that the client will allow for no data to be available on the socket before closing the underlying connection, 'SO_TIMEOUT' in socket terms. A timeout of zero is interpreted as an infinite timeout. A negative value for or the absence of this setting is interpreted as undefined (system default). * :ssl-protocols - used to set the list of SSL protocols that the client could select from when talking to the server. Defaults to 'TLSv1', 'TLSv1.1', and 'TLSv1.2'. * :cipher-suites - used to set the cipher suites that the client could select from when talking to the server. Defaults to the complete set of suites supported by the underlying language runtime. * :metric-registry - a MetricRegistry instance used to collect metrics on client requests. opts (ssl-specific where only one of the following combinations permitted): * :ssl-context - an instance of SSLContext OR * :ssl-cert - path to a PEM file containing the client cert * :ssl-key - path to a PEM file containing the client private key * :ssl-ca-cert - path to a PEM file containing the CA cert OR * :ssl-ca-cert - path to a PEM file containing the CA cert" [opts :- common/ClientOptions] (let [client (create-default-client opts) metric-registry (:metric-registry opts) metric-namespace (metrics/build-metric-namespace (:metric-prefix opts) (:server-id opts)) enable-url-metrics? (clojure.core/get opts :enable-url-metrics? true)] (reify common/HTTPClient (get [this url] (common/get this url {})) (get [this url opts] (common/make-request this url :get opts)) (head [this url] (common/head this url {})) (head [this url opts] (common/make-request this url :head opts)) (post [this url] (common/post this url {})) (post [this url opts] (common/make-request this url :post opts)) (put [this url] (common/put this url {})) (put [this url opts] (common/make-request this url :put opts)) (delete [this url] (common/delete this url {})) (delete [this url opts] (common/make-request this url :delete opts)) (trace [this url] (common/trace this url {})) (trace [this url opts] (common/make-request this url :trace opts)) (options [this url] (common/options this url {})) (options [this url opts] (common/make-request this url :options opts)) (patch [this url] (common/patch this url {})) (patch [this url opts] (common/make-request this url :patch opts)) (make-request [this url method] (common/make-request this url method {})) (make-request [_ url method opts] (request-with-client (assoc opts :method method :url url) nil client metric-registry metric-namespace enable-url-metrics?)) (close [_] (.close client)) (get-client-metric-registry [_] metric-registry) (get-client-metric-namespace [_] metric-namespace)))) clj-http-client-1.2.0/src/clj/puppetlabs/http/client/common.clj000066400000000000000000000144111373223535200244560ustar00rootroot00000000000000(ns puppetlabs.http.client.common (:import (java.net URL URI) (javax.net.ssl SSLContext) (com.codahale.metrics MetricRegistry) (clojure.lang IBlockingDeref) (java.io InputStream) (java.nio.charset Charset) (com.puppetlabs.http.client.metrics ClientTimer)) (:require [schema.core :as schema]) (:refer-clojure :exclude (get))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Client Protocol (defprotocol HTTPClient (get [this url] [this url opts]) (head [this url] [this url opts]) (post [this url] [this url opts]) (put [this url] [this url opts]) (delete [this url] [this url opts]) (trace [this url] [this url opts]) (options [this url] [this url opts]) (patch [this url] [this url opts]) (make-request [this url method] [this url method opts]) (close [this]) (get-client-metric-registry [this]) (get-client-metric-namespace [this])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Schemas (def ok schema/optional-key) (def UrlOrUriOrString (schema/either schema/Str URL URI)) (def UrlOrString (schema/either schema/Str URL)) (def Headers {schema/Str schema/Str}) (def Body (schema/maybe (schema/either String InputStream))) (def BodyType (schema/enum :text :stream :unbuffered-stream)) (def CompressType (schema/enum :gzip :none)) (def MetricId [(schema/either schema/Str schema/Keyword)]) (def RawUserRequestClientOptions "The list of request and client options passed by a user into the request function. Allows the user to configure both a client and a request." {:url UrlOrUriOrString :method schema/Keyword (ok :headers) Headers (ok :body) Body (ok :decompress-body) schema/Bool (ok :compress-request-body) CompressType (ok :as) BodyType (ok :query-params) {schema/Str schema/Str} (ok :metric-id) [schema/Str] (ok :ssl-context) SSLContext (ok :ssl-cert) UrlOrString (ok :ssl-key) UrlOrString (ok :ssl-ca-cert) UrlOrString (ok :ssl-protocols) [schema/Str] (ok :cipher-suites) [schema/Str] (ok :force-redirects) schema/Bool (ok :follow-redirects) schema/Bool (ok :connect-timeout-milliseconds) schema/Int (ok :socket-timeout-milliseconds) schema/Int}) (def RawUserRequestOptions "The list of request options passed by a user into the request function. Allows the user to configure a request." {:url UrlOrUriOrString :method schema/Keyword (ok :headers) Headers (ok :body) Body (ok :decompress-body) schema/Bool (ok :compress-request-body) CompressType (ok :as) BodyType (ok :query-params) {schema/Str schema/Str} (ok :metric-id) MetricId}) (def RequestOptions "The options from UserRequestOptions that have to do with the configuration and settings for an individual request. This is everything from UserRequestOptions not included in ClientOptions." {:url UrlOrUriOrString :method schema/Keyword :headers Headers :body Body :decompress-body schema/Bool :compress-request-body CompressType :as BodyType (ok :query-params) {schema/Str schema/Str} (ok :metric-id) MetricId}) (def SslContextOptions {:ssl-context SSLContext}) (def SslCaCertOptions {:ssl-ca-cert UrlOrString}) (def SslCertOptions {:ssl-cert UrlOrString :ssl-key UrlOrString :ssl-ca-cert UrlOrString}) (def SslProtocolOptions {(ok :ssl-protocols) [schema/Str] (ok :cipher-suites) [schema/Str]}) (def BaseClientOptions {(ok :force-redirects) schema/Bool (ok :follow-redirects) schema/Bool (ok :connect-timeout-milliseconds) schema/Int (ok :socket-timeout-milliseconds) schema/Int (ok :metric-registry) MetricRegistry (ok :server-id) schema/Str (ok :metric-prefix) schema/Str (ok :enable-url-metrics?) schema/Bool (ok :max-connections-total) schema/Int (ok :max-connections-per-route) schema/Int}) (def UserRequestOptions "A cleaned-up version of RawUserRequestClientOptions, which is formed after validating the RawUserRequestClientOptions and merging it with the defaults." (schema/either (merge RequestOptions BaseClientOptions) (merge RequestOptions SslContextOptions SslProtocolOptions BaseClientOptions) (merge RequestOptions SslCaCertOptions SslProtocolOptions BaseClientOptions) (merge RequestOptions SslCertOptions SslProtocolOptions BaseClientOptions))) (def ClientOptions "The options from UserRequestOptions that are related to the instantiation/management of a client. This is everything from UserRequestOptions not included in RequestOptions." (schema/either BaseClientOptions (merge SslContextOptions SslProtocolOptions BaseClientOptions) (merge SslCertOptions SslProtocolOptions BaseClientOptions) (merge SslCaCertOptions SslProtocolOptions BaseClientOptions))) (def ResponseCallbackFn (schema/maybe (schema/pred ifn?))) (def ResponsePromise IBlockingDeref) (def ContentType (schema/maybe {:mime-type schema/Str :charset (schema/maybe Charset)})) (def NormalResponse {:opts UserRequestOptions :orig-content-encoding (schema/maybe schema/Str) :body Body :headers Headers :status schema/Int :reason-phrase schema/Str :content-type ContentType}) (def ErrorResponse {:opts UserRequestOptions :error Exception}) (def Response (schema/either NormalResponse ErrorResponse)) (def HTTPMethod (schema/enum :delete :get :head :option :patch :post :put :trace)) (def Metrics [ClientTimer]) (def AllMetrics {:url Metrics :url-and-method Metrics :metric-id Metrics}) (def BaseMetricData {:metric-name schema/Str :count schema/Int :mean schema/Num :aggregate schema/Num}) (def UrlMetricData (assoc BaseMetricData :url schema/Str)) (def UrlAndMethodMetricData (assoc UrlMetricData :method schema/Str)) (def MetricIdMetricData (assoc BaseMetricData :metric-id [schema/Str])) (def AllMetricsData {:url [UrlMetricData] :url-and-method [UrlAndMethodMetricData] :metric-id [MetricIdMetricData]}) (def MetricTypes (schema/enum :full-response)) clj-http-client-1.2.0/src/clj/puppetlabs/http/client/metrics.clj000066400000000000000000000111161373223535200246330ustar00rootroot00000000000000(ns puppetlabs.http.client.metrics (:require [schema.core :as schema] [puppetlabs.http.client.common :as common]) (:import (com.puppetlabs.http.client.metrics Metrics$MetricType Metrics ClientMetricData) (com.codahale.metrics MetricRegistry))) (schema/defn get-base-metric-data :- common/BaseMetricData [data :- ClientMetricData] {:count (.getCount data) :mean (.getMean data) :aggregate (.getAggregate data) :metric-name (.getMetricName data)}) (schema/defn get-url-metric-data :- common/UrlMetricData [data :- ClientMetricData] (assoc (get-base-metric-data data) :url (.getUrl data))) (schema/defn get-url-and-method-metric-data :- common/UrlAndMethodMetricData [data :- ClientMetricData] (assoc (get-url-metric-data data) :method (.getMethod data))) (schema/defn get-metric-id-metric-data :- common/MetricIdMetricData [data :- ClientMetricData] (assoc (get-base-metric-data data) :metric-id (.getMetricId data))) (defn get-java-metric-type [metric-type] (case metric-type :full-response Metrics$MetricType/FULL_RESPONSE)) (defn uppercase-method [method] (clojure.string/upper-case (name method))) (defn build-metric-namespace [metric-prefix server-id] (Metrics/buildMetricNamespace metric-prefix server-id)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Public (schema/defn ^:always-validate url->metric-url :- schema/Str [url :- schema/Str] (Metrics/urlToMetricUrl url)) (schema/defn ^:always-validate get-client-metrics :- (schema/maybe common/AllMetrics) "Returns the http client-specific metrics from the metric registry." [metric-registry :- MetricRegistry] (let [metrics (Metrics/getClientMetrics metric-registry)] {:url (.getUrlTimers metrics) :url-and-method (.getUrlAndMethodTimers metrics) :metric-id (.getMetricIdTimers metrics)})) (schema/defn ^:always-validate get-client-metrics-by-url :- common/Metrics "Returns the http client-specific url metrics matching the specified url." [metric-registry :- MetricRegistry url :- schema/Str] (Metrics/getClientMetricsByUrl metric-registry url)) (schema/defn ^:always-validate get-client-metrics-by-url-and-method :- common/Metrics "Returns the http client-specific url metrics matching the specified url." [metric-registry :- MetricRegistry url :- schema/Str method :- common/HTTPMethod] (Metrics/getClientMetricsByUrlAndMethod metric-registry url method)) (schema/defn ^:always-validate get-client-metrics-by-metric-id :- common/Metrics "Returns the http client-specific url metrics matching the specified url." [metric-registry :- MetricRegistry metric-id :- common/MetricId] (Metrics/getClientMetricsByMetricId metric-registry (into-array String (map name metric-id)))) (schema/defn ^:always-validate get-client-metrics-data :- common/AllMetricsData "Returns a summary of the metric data for all http client timers, organized in a map by category." [metric-registry :- MetricRegistry] (let [data (Metrics/getClientMetricsData metric-registry)] {:url (map get-url-metric-data (.getUrlData data)) :url-and-method (map get-url-and-method-metric-data (.getUrlAndMethodData data)) :metric-id (map get-metric-id-metric-data (.getMetricIdData data))})) (schema/defn ^:always-validate get-client-metrics-data-by-url :- [common/UrlMetricData] "Returns a summary of the metric data for all http client timers filtered by url." [metric-registry :- MetricRegistry url :- schema/Str] (let [data (Metrics/getClientMetricsDataByUrl metric-registry url)] (map get-url-metric-data data))) (schema/defn ^:always-validate get-client-metrics-data-by-url-and-method :- [common/UrlAndMethodMetricData] "Returns a summary of the metric data for all http client timers filtered by url and method." [metric-registry :- MetricRegistry url :- schema/Str method :- common/HTTPMethod] (let [data (Metrics/getClientMetricsDataByUrlAndMethod metric-registry url (uppercase-method method))] (map get-url-and-method-metric-data data))) (schema/defn ^:always-validate get-client-metrics-data-by-metric-id :- [common/MetricIdMetricData] "Returns a summary of the metric data for all http client timers filtered by metric-id." [metric-registry :- MetricRegistry metric-id :- common/MetricId] (let [data (Metrics/getClientMetricsDataByMetricId metric-registry (into-array String (map name metric-id)))] (map get-metric-id-metric-data data))) clj-http-client-1.2.0/src/clj/puppetlabs/http/client/sync.clj000066400000000000000000000124701373223535200241450ustar00rootroot00000000000000;; This namespace provides synchronous versions of the request functions ;; defined in puppetlabs.http.client (ns puppetlabs.http.client.sync (:require [puppetlabs.http.client.async :as async] [schema.core :as schema] [puppetlabs.http.client.common :as common] [puppetlabs.http.client.metrics :as metrics]) (:refer-clojure :exclude (get))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Private utility functions (schema/defn extract-client-opts :- common/ClientOptions [opts :- common/RawUserRequestClientOptions] (select-keys opts [:ssl-context :ssl-ca-cert :ssl-cert :ssl-key :ssl-protocols :cipher-suites :force-redirects :follow-redirects :connect-timeout-milliseconds :socket-timeout-milliseconds :max-connections-per-route :max-connections-total])) (schema/defn extract-request-opts :- common/RawUserRequestOptions [opts :- common/RawUserRequestClientOptions] (select-keys opts [:url :method :headers :body :decompress-body :compress-request-body :as :query-params])) (defn request-with-client ([req client] (request-with-client req client nil nil true)) ([req client metric-registry metric-namespace] (request-with-client req client metric-registry metric-namespace true)) ([req client metric-registry metric-namespace enable-url-metrics?] (let [{:keys [error] :as resp} @(async/request-with-client req nil client metric-registry metric-namespace enable-url-metrics?)] (if error (throw error) resp)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Public (defn request [req] (with-open [client (async/create-default-client (extract-client-opts req))] (request-with-client (extract-request-opts req) client))) (schema/defn create-client :- (schema/protocol common/HTTPClient) [opts :- common/ClientOptions] (let [client (async/create-default-client opts) metric-registry (:metric-registry opts) metric-namespace (metrics/build-metric-namespace (:metric-prefix opts) (:server-id opts)) enable-url-metrics? (clojure.core/get opts :enable-url-metrics? true)] (reify common/HTTPClient (get [this url] (common/get this url {})) (get [this url opts] (common/make-request this url :get opts)) (head [this url] (common/head this url {})) (head [this url opts] (common/make-request this url :head opts)) (post [this url] (common/post this url {})) (post [this url opts] (common/make-request this url :post opts)) (put [this url] (common/put this url {})) (put [this url opts] (common/make-request this url :put opts)) (delete [this url] (common/delete this url {})) (delete [this url opts] (common/make-request this url :delete opts)) (trace [this url] (common/trace this url {})) (trace [this url opts] (common/make-request this url :trace opts)) (options [this url] (common/options this url {})) (options [this url opts] (common/make-request this url :post opts)) (patch [this url] (common/patch this url {})) (patch [this url opts] (common/make-request this url :patch opts)) (make-request [this url method] (common/make-request this url method {})) (make-request [_ url method opts] (request-with-client (assoc opts :method method :url url) client metric-registry metric-namespace enable-url-metrics?)) (close [_] (.close client)) (get-client-metric-registry [_] metric-registry) (get-client-metric-namespace [_] metric-namespace)))) (defn get "Issue a synchronous HTTP GET request. This will raise an exception if an error is returned." ([url] (get url {})) ([url opts] (request (assoc opts :method :get :url url)))) (defn head "Issue a synchronous HTTP head request. This will raise an exception if an error is returned." ([url] (head url {})) ([url opts] (request (assoc opts :method :head :url url)))) (defn post "Issue a synchronous HTTP POST request. This will raise an exception if an error is returned." ([url] (post url {})) ([url opts] (request (assoc opts :method :post :url url)))) (defn put "Issue a synchronous HTTP PUT request. This will raise an exception if an error is returned." ([url] (put url {})) ([url opts] (request (assoc opts :method :put :url url)))) (defn delete "Issue a synchronous HTTP DELETE request. This will raise an exception if an error is returned." ([url] (delete url {})) ([url opts] (request (assoc opts :method :delete :url url)))) (defn trace "Issue a synchronous HTTP TRACE request. This will raise an exception if an error is returned." ([url] (trace url {})) ([url opts] (request (assoc opts :method :trace :url url)))) (defn options "Issue a synchronous HTTP OPTIONS request. This will raise an exception if an error is returned." ([url] (options url {})) ([url opts] (request (assoc opts :method :options :url url)))) (defn patch "Issue a synchronous HTTP PATCH request. This will raise an exception if an error is returned." ([url] (patch url {})) ([url opts] (request (assoc opts :method :patch :url url)))) clj-http-client-1.2.0/src/java/000077500000000000000000000000001373223535200162305ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/000077500000000000000000000000001373223535200170065ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/000077500000000000000000000000001373223535200211655ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/000077500000000000000000000000001373223535200221445ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/000077500000000000000000000000001373223535200234225ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/Async.java000066400000000000000000000021431373223535200253420ustar00rootroot00000000000000package com.puppetlabs.http.client; import com.puppetlabs.http.client.impl.JavaClient; import com.puppetlabs.http.client.impl.PersistentAsyncHttpClient; import com.puppetlabs.http.client.metrics.Metrics; /** * This class allows you to create an AsyncHttpClient for use in making * HTTP Requests. It consists exclusively of a static method to create * a client. */ public class Async { /** * Allows you to create an instance of an AsyncHttpClient for use in * making HTTP requests. * * @param clientOptions the list of options with which to configure the client * @return an AsyncHttpClient that can be used to make requests */ public static AsyncHttpClient createClient(ClientOptions clientOptions) { final String metricNamespace = Metrics.buildMetricNamespace(clientOptions.getMetricPrefix(), clientOptions.getServerId()); return new PersistentAsyncHttpClient(JavaClient.createClient(clientOptions), clientOptions.getMetricRegistry(), metricNamespace, clientOptions.isEnableURLMetrics()); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/AsyncHttpClient.java000066400000000000000000000150211373223535200273400ustar00rootroot00000000000000package com.puppetlabs.http.client; import com.codahale.metrics.MetricRegistry; import com.puppetlabs.http.client.impl.Promise; import java.io.Closeable; import java.net.URI; import java.net.URISyntaxException; /** * This interface represents an asynchronous HTTP client with which * requests can be made. An object implementing this interface is returned by * {@link com.puppetlabs.http.client.Async#createClient(ClientOptions)}. */ public interface AsyncHttpClient extends Closeable{ /** * @return the MetricRegistry instance associated with this Client */ public MetricRegistry getMetricRegistry(); /** * @return the String metricNamespace for this Client */ public String getMetricNamespace(); /** * Performs a GET request * @param url the URL against which to make the GET request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise get(String url) throws URISyntaxException; /** * Performs a GET request * @param uri the URI against which to make the GET request * @return a Promise with the contents of the response */ public Promise get(URI uri); /** * Performs a GET request * @param requestOptions options to configure the GET request * @return a Promise with the contents of the response */ public Promise get(RequestOptions requestOptions); /** * Performs a HEAD request * @param url the URL against which to make the HEAD request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise head(String url) throws URISyntaxException; /** * Performs a HEAD request * @param uri the URI against which to make the HEAD request * @return a Promise with the contents of the response */ public Promise head(URI uri); /** * Performs a HEAD request * @param requestOptions options to configure the HEAD request * @return a Promise with the contents of the response */ public Promise head(RequestOptions requestOptions); /** * Performs a POST request * @param url the URL against which to make the POST request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise post(String url) throws URISyntaxException; /** * Performs a POST request * @param uri the URI against which to make the POST request * @return a Promise with the contents of the response */ public Promise post(URI uri); /** * Performs a POST request * @param requestOptions options to configure the POST request * @return a Promise with the contents of the response */ public Promise post(RequestOptions requestOptions); /** * Performs a PUT request * @param url the URL against which to make the PUT request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise put(String url) throws URISyntaxException; /** * Performs a PUT request * @param uri the URI against which to make the PUT request * @return a Promise with the contents of the response */ public Promise put(URI uri); /** * Performs a PUT request * @param requestOptions options to configure the PUT request * @return a Promise with the contents of the response */ public Promise put(RequestOptions requestOptions); /** * Performs a DELETE request * @param url the URL against which to make the DELETE request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise delete(String url) throws URISyntaxException; /** * Performs a DELETE request * @param uri the URI against which to make the DELETE request * @return a Promise with the contents of the response */ public Promise delete(URI uri); /** * Performs a DELETE request * @param requestOptions options to configure the DELETE request * @return a Promise with the contents of the response */ public Promise delete(RequestOptions requestOptions); /** * Performs a TRACE request * @param url the URL against which to make the TRACE request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise trace(String url) throws URISyntaxException; /** * Performs a TRACE request * @param uri the URI against which to make the TRACE request * @return a Promise with the contents of the response */ public Promise trace(URI uri); /** * Performs a TRACE request * @param requestOptions options to configure the TRACE request * @return a Promise with the contents of the response */ public Promise trace(RequestOptions requestOptions); /** * Performs an OPTIONS request * @param url the URL against which to make the OPTIONS request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise options(String url) throws URISyntaxException; /** * Performs an OPTIONS request * @param uri the URI against which to make the OPTIONS request * @return a Promise with the contents of the response */ public Promise options(URI uri); /** * Performs an OPTIONS request * @param requestOptions options to configure the OPTIONS request * @return a Promise with the contents of the response */ public Promise options(RequestOptions requestOptions); /** * Performs a PATCH request * @param url the URL against which to make the PATCH request * @return a Promise with the contents of the response * @throws URISyntaxException */ public Promise patch(String url) throws URISyntaxException; /** * Performs a PATCH request * @param uri the URI against which to make the PATCH request * @return a Promise with the contents of the response */ public Promise patch(URI uri); /** * Performs a PATCH request * @param requestOptions options to configure the PATCH request * @return a Promise with the contents of the response */ public Promise patch(RequestOptions requestOptions); } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/ClientOptions.java000066400000000000000000000215111373223535200270570ustar00rootroot00000000000000package com.puppetlabs.http.client; import com.codahale.metrics.MetricRegistry; import com.puppetlabs.ssl_utils.SSLUtils; import javax.net.ssl.SSLContext; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; import java.security.Security; /** * This class is a wrapper around a number of options for use * in configuring a persistent HTTP Client. * * @see com.puppetlabs.http.client.Async#createClient(ClientOptions) * @see com.puppetlabs.http.client.Sync#createClient(ClientOptions) */ public class ClientOptions { public static final String[] DEFAULT_SSL_PROTOCOLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}; private SSLContext sslContext; private String sslCert; private String sslKey; private String sslCaCert; private String[] sslProtocols; private String[] sslCipherSuites; private boolean insecure = false; private boolean forceRedirects = false; private boolean followRedirects = true; private int connectTimeoutMilliseconds = -1; private int socketTimeoutMilliseconds = -1; private MetricRegistry metricRegistry; private String metricPrefix; private String serverId; private boolean enableURLMetrics = true; // defaults from apache connection manager private int maxConnectionsTotal = 20; private int maxConnectionsPerRoute = 2; /** * Constructor for the ClientOptions class. When this constructor is called, * insecure and forceRedirects will default to false, and followRedirects will default * to true. */ public ClientOptions() { } /** * Constructor for the ClientOptions class. * @param sslContext The SSL context for the client. * @param sslCert The path to a PEM file containing the client cert * @param sslKey The path to a PEM file containing the client private key * @param sslCaCert The path to a PEM file containing the CA cert * @param sslProtocols The SSL protocols that the client can select from when talking to the server * @param sslCipherSuites The cipher suites that the client can select from when talking to the server * @param insecure Whether or not the client should use an insecure connection. * @param forceRedirects Whether or not the client should follow redirects on POST or PUT requests. * @param followRedirects Whether or not the client should follow redirects in general. * @param connectTimeoutMilliseconds Maximum number of milliseconds that * the client will wait for a * connection to be established. A * value of zero is interpreted as * infinite. A negative value is * interpreted as undefined (system * default). * @param socketTimeoutMilliseconds Maximum number of milliseconds that * the client will allow for no data to * be available on the socket before * closing the underlying connection, * SO_TIMEOUT in socket * terms. A timeout of zero is * interpreted as an infinite timeout. * A negative value is interpreted as * undefined (system default). * @param maxConnectionsPerRoute Maxiumum number of connections allowed for a given route for a client instance, where * a route is a host / port combination. The apache default is 2 * @param maxConnectionsTotal Maximum number of connections allowed for a given client instance. The apache default * is 20. * */ public ClientOptions(SSLContext sslContext, String sslCert, String sslKey, String sslCaCert, String[] sslProtocols, String[] sslCipherSuites, boolean insecure, boolean forceRedirects, boolean followRedirects, int connectTimeoutMilliseconds, int socketTimeoutMilliseconds, int maxConnectionsPerRoute, int maxConnectionsTotal) { this.sslContext = sslContext; this.sslCert = sslCert; this.sslKey = sslKey; this.sslCaCert = sslCaCert; this.sslProtocols = sslProtocols; this.sslCipherSuites = sslCipherSuites; this.insecure = insecure; this.forceRedirects = forceRedirects; this.followRedirects = followRedirects; this.connectTimeoutMilliseconds = connectTimeoutMilliseconds; this.socketTimeoutMilliseconds = socketTimeoutMilliseconds; this.maxConnectionsPerRoute = maxConnectionsPerRoute; this.maxConnectionsTotal = maxConnectionsTotal; } public SSLContext getSslContext() { return sslContext; } public ClientOptions setSslContext(SSLContext sslContext) { this.sslContext = sslContext; return this; } public String getSslCert() { return sslCert; } public ClientOptions setSslCert(String sslCert) { this.sslCert = sslCert; return this; } public String getSslKey() { return sslKey; } public ClientOptions setSslKey(String sslKey) { this.sslKey = sslKey; return this; } public String getSslCaCert() { return sslCaCert; } public ClientOptions setSslCaCert(String sslCaCert) { this.sslCaCert = sslCaCert; return this; } public String[] getSslProtocols() { return sslProtocols; } public ClientOptions setSslProtocols(String[] sslProtocols) { this.sslProtocols = sslProtocols; return this; } public String[] getSslCipherSuites() { return sslCipherSuites; } public ClientOptions setSslCipherSuites(String[] sslCipherSuites) { this.sslCipherSuites = sslCipherSuites; return this; } public boolean getInsecure() { return insecure; } public ClientOptions setInsecure(boolean insecure) { this.insecure = insecure; return this; } public boolean getForceRedirects() { return forceRedirects; } public ClientOptions setForceRedirects(boolean forceRedirects) { this.forceRedirects = forceRedirects; return this; } public boolean getFollowRedirects() { return followRedirects; } public ClientOptions setFollowRedirects(boolean followRedirects) { this.followRedirects = followRedirects; return this; } public int getConnectTimeoutMilliseconds() { return connectTimeoutMilliseconds; } public ClientOptions setConnectTimeoutMilliseconds( int connectTimeoutMilliseconds) { this.connectTimeoutMilliseconds = connectTimeoutMilliseconds; return this; } public int getSocketTimeoutMilliseconds() { return socketTimeoutMilliseconds; } public ClientOptions setSocketTimeoutMilliseconds( int socketTimeoutMilliseconds) { this.socketTimeoutMilliseconds = socketTimeoutMilliseconds; return this; } public MetricRegistry getMetricRegistry() { return metricRegistry; } public ClientOptions setMetricRegistry(MetricRegistry metricRegistry) { this.metricRegistry = metricRegistry; return this; } public String getMetricPrefix() { return metricPrefix; } public ClientOptions setMetricPrefix(String metricPrefix) { this.metricPrefix = metricPrefix; return this; } public String getServerId() { return serverId; } public ClientOptions setServerId(String serverId) { this.serverId = serverId; return this; } public boolean isEnableURLMetrics() { return enableURLMetrics; } public ClientOptions setEnableURLMetrics(boolean enableURLMetrics) { this.enableURLMetrics = enableURLMetrics; return this; } public int getMaxConnectionsTotal() {return this.maxConnectionsTotal; } public ClientOptions setMaxConnectionsTotal(int maxConnectionsTotal) { this.maxConnectionsTotal = maxConnectionsTotal; return this; } public int getMaxConnectionsPerRoute() {return this.maxConnectionsPerRoute; } public ClientOptions setMaxConnectionsPerRoute(int maxConnectionsPerRoute) { this.maxConnectionsPerRoute = maxConnectionsPerRoute; return this; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/CompressType.java000066400000000000000000000001251373223535200267200ustar00rootroot00000000000000package com.puppetlabs.http.client; public enum CompressType { GZIP, NONE } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/HttpClientException.java000066400000000000000000000005301373223535200302200ustar00rootroot00000000000000package com.puppetlabs.http.client; /** * This class represents an exception caused by an error in * an HTTP request */ public class HttpClientException extends RuntimeException { public HttpClientException(String msg) { super(msg); } public HttpClientException(String msg, Throwable t) { super(msg, t); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/HttpMethod.java000066400000000000000000000012461373223535200263500ustar00rootroot00000000000000package com.puppetlabs.http.client; import org.apache.http.client.methods.*; /** * This enum represents the various HTTP methods that can be used to make requests. */ public enum HttpMethod { GET(HttpGet.class), HEAD(HttpHead.class), POST(HttpPost.class), PUT(HttpPut.class), DELETE(HttpDelete.class), TRACE(HttpTrace.class), OPTIONS(HttpOptions.class), PATCH(HttpPatch.class); private Class httpMethod; HttpMethod(Class httpMethod) { this.httpMethod = httpMethod; } public Class getValue() { return this.httpMethod; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/RequestOptions.java000066400000000000000000000066721373223535200273040ustar00rootroot00000000000000package com.puppetlabs.http.client; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; /** * This class is a wrapper around a number of options for use in * configuring an HTTP request. */ public class RequestOptions { private URI uri; private Map headers; private Object body; private CompressType requestBodyCompression = CompressType.NONE; private boolean decompressBody = true; private ResponseBodyType as = ResponseBodyType.STREAM; private String[] metricId; /** * Constructor for the RequestOptions class. When this constructor is called, * decompressBody will default to true, and as will default to ResponseBodyType.STREAM * @param url the URL against which to make the request * @throws URISyntaxException */ public RequestOptions (String url) throws URISyntaxException { this.uri = new URI(url); } /** * Constructor for the RequestOptions class. When this constructor is called, * decompressBody will default to true, and as will default to ResponseBodyType.STREAM * @param uri the URI against which to make the request */ public RequestOptions(URI uri) { this.uri = uri; } /** * Constructor for the RequestOptions class * @param uri the URI against which to make the request * @param headers A map of headers. Can be null. * @param body The body of the request. Can be null. * @param decompressBody If true, an "accept-encoding" header with a value of "gzip, deflate" will be * automatically decompressed if it contains a recognized "content-encoding" header. * @param as Used to control the data type of the response body. */ public RequestOptions (URI uri, Map headers, Object body, boolean decompressBody, ResponseBodyType as) { this.uri = uri; this.headers = headers; this.body = body; this.decompressBody = decompressBody; this.as = as; } public URI getUri() { return uri; } public RequestOptions setUri(URI uri) { this.uri = uri; return this; } public Map getHeaders() { return headers; } public RequestOptions setHeaders(Map headers) { this.headers = headers; return this; } public Object getBody() { return body; } public RequestOptions setBody(Object body) { this.body = body; return this; } public boolean getDecompressBody() { return decompressBody; } public RequestOptions setDecompressBody(boolean decompressBody) { this.decompressBody = decompressBody; return this; } public CompressType getCompressRequestBody() { return requestBodyCompression; } public RequestOptions setCompressRequestBody( CompressType requestBodyCompression) { this.requestBodyCompression = requestBodyCompression; return this; } public ResponseBodyType getAs() { return as; } public RequestOptions setAs(ResponseBodyType as) { this.as = as; return this; } public String[] getMetricId() { return metricId; } public RequestOptions setMetricId(String[] metricId) { this.metricId = metricId; return this; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/Response.java000066400000000000000000000032111373223535200260600ustar00rootroot00000000000000package com.puppetlabs.http.client; import com.puppetlabs.http.client.RequestOptions; import org.apache.http.entity.ContentType; import java.util.Map; /** * This class represents a response from an HTTP request. */ public class Response { private RequestOptions options; private String origContentEncoding; private Throwable error; private Object body; private Map headers; private Integer status; private String reasonPhrase; private ContentType contentType; public Response(RequestOptions options, Throwable error) { this.options = options; this.error = error; } public Response(RequestOptions options, String origContentEncoding, Object body, Map headers, int status, String reasonPhrase, ContentType contentType) { this.options = options; this.origContentEncoding = origContentEncoding; this.body = body; this.headers = headers; this.status = status; this.reasonPhrase = reasonPhrase; this.contentType = contentType; } public RequestOptions getOptions() { return options; } public String getOrigContentEncoding() { return origContentEncoding; } public Throwable getError() { return error; } public Object getBody() { return body; } public Map getHeaders() { return headers; } public Integer getStatus() { return status; } public String getReasonPhrase() { return reasonPhrase; } public ContentType getContentType() { return contentType; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/ResponseBodyType.java000066400000000000000000000003011373223535200275350ustar00rootroot00000000000000package com.puppetlabs.http.client; /** * This Enum represents the possible types of the body of a response. */ public enum ResponseBodyType { UNBUFFERED_STREAM, STREAM, TEXT; } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/SimpleRequestOptions.java000066400000000000000000000136721373223535200304540ustar00rootroot00000000000000package com.puppetlabs.http.client; import com.puppetlabs.ssl_utils.SSLUtils; import javax.net.ssl.SSLContext; import java.net.URI; import java.net.URISyntaxException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Map; /** * This class represents is a wrapper around a number of options for use in * making requests with the simple request functions contained in the Sync class. * It is a combination of the options from ClientOptions and RequestOptions. * * @see com.puppetlabs.http.client.ClientOptions#ClientOptions(javax.net.ssl.SSLContext, String, String, String, String[], String[], boolean, boolean, boolean, int, int, int, int) * @see com.puppetlabs.http.client.RequestOptions#RequestOptions(java.net.URI, java.util.Map, Object, boolean, ResponseBodyType) */ public class SimpleRequestOptions { private URI uri; private Map headers; private SSLContext sslContext; private String sslCert; private String sslKey; private String sslCaCert; private String[] sslProtocols; private String[] sslCipherSuites; private boolean insecure = false; private Object body; private CompressType requestBodyCompression = CompressType.NONE; private boolean decompressBody = true; private ResponseBodyType as = ResponseBodyType.STREAM; private boolean forceRedirects = false; private boolean followRedirects = true; private int connectTimeoutMilliseconds = -1; private int socketTimeoutMilliseconds = -1; /** * Constructor for SimpleRequestOptions. When this constructor is used, * insecure and forceRedirects default to false, and followRedirects and decompressBody * default to true. as defaults to ResponseBodyType.STREAM. * @param url the URL against which to make the HTTP request * @throws URISyntaxException */ public SimpleRequestOptions (String url) throws URISyntaxException { this.uri = new URI(url); } /** * Constructor for SimpleRequestOptions. When this constructor is used, * insecure and forceRedirects default to false, and followRedirects and decompressBody * default to true. as defaults to ResponseBodyType.STREAM. * @param uri the URI against which to make the HTTP request */ public SimpleRequestOptions(URI uri) { this.uri = uri; } public URI getUri() { return uri; } public SimpleRequestOptions setUri(URI uri) { this.uri = uri; return this; } public Map getHeaders() { return headers; } public SimpleRequestOptions setHeaders(Map headers) { this.headers = headers; return this; } public SSLContext getSslContext() { return sslContext; } public SimpleRequestOptions setSslContext(SSLContext sslContext) { this.sslContext = sslContext; return this; } public String getSslCert() { return sslCert; } public SimpleRequestOptions setSslCert(String sslCert) { this.sslCert = sslCert; return this; } public String getSslKey() { return sslKey; } public SimpleRequestOptions setSslKey(String sslKey) { this.sslKey = sslKey; return this; } public String getSslCaCert() { return sslCaCert; } public SimpleRequestOptions setSslCaCert(String sslCaCert) { this.sslCaCert = sslCaCert; return this; } public String[] getSslProtocols() { return sslProtocols; } public SimpleRequestOptions setSslProtocols(String[] sslProtocols) { this.sslProtocols = sslProtocols; return this; } public String[] getSslCipherSuites() { return sslCipherSuites; } public SimpleRequestOptions setSslCipherSuites(String[] sslCipherSuites) { this.sslCipherSuites = sslCipherSuites; return this; } public boolean getInsecure() { return insecure; } public SimpleRequestOptions setInsecure(boolean insecure) { this.insecure = insecure; return this; } public Object getBody() { return body; } public SimpleRequestOptions setBody(Object body) { this.body = body; return this; } public boolean getDecompressBody() { return decompressBody; } public SimpleRequestOptions setDecompressBody(boolean decompressBody) { this.decompressBody = decompressBody; return this; } public CompressType getCompressRequestBody() { return requestBodyCompression; } public SimpleRequestOptions setRequestBodyCompression( CompressType requestBodyCompression) { this.requestBodyCompression = requestBodyCompression; return this; } public ResponseBodyType getAs() { return as; } public SimpleRequestOptions setAs(ResponseBodyType as) { this.as = as; return this; } public boolean getForceRedirects() { return forceRedirects; } public SimpleRequestOptions setForceRedirects(boolean forceRedirects) { this.forceRedirects = forceRedirects; return this; } public boolean getFollowRedirects() { return followRedirects; } public SimpleRequestOptions setFollowRedirects(boolean followRedirects) { this.followRedirects = followRedirects; return this; } public int getConnectTimeoutMilliseconds() { return connectTimeoutMilliseconds; } public SimpleRequestOptions setConnectTimeoutMilliseconds( int connectTimeoutMilliseconds) { this.connectTimeoutMilliseconds = connectTimeoutMilliseconds; return this; } public int getSocketTimeoutMilliseconds() { return socketTimeoutMilliseconds; } public SimpleRequestOptions setSocketTimeoutMilliseconds( int socketTimeoutMilliseconds) { this.socketTimeoutMilliseconds = socketTimeoutMilliseconds; return this; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/Sync.java000066400000000000000000000271211373223535200252040ustar00rootroot00000000000000package com.puppetlabs.http.client; import com.puppetlabs.http.client.impl.JavaClient; import com.puppetlabs.http.client.impl.PersistentSyncHttpClient; import com.puppetlabs.http.client.metrics.Metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; /** * This class allows for the creation of a persistent synchronous HTTP client. It also allows * for sending synchronous HTTP requests without a persistent HTTP client. */ public class Sync { private static final Logger LOGGER = LoggerFactory.getLogger(Sync.class); private static RequestOptions extractRequestOptions(SimpleRequestOptions simpleOptions) { URI uri = simpleOptions.getUri(); Map headers = simpleOptions.getHeaders(); Object body = simpleOptions.getBody(); boolean decompressBody = simpleOptions.getDecompressBody(); ResponseBodyType as = simpleOptions.getAs(); CompressType requestBodyDecompression = simpleOptions.getCompressRequestBody(); RequestOptions requestOptions = new RequestOptions( uri, headers, body, decompressBody, as); requestOptions.setCompressRequestBody(requestBodyDecompression); return requestOptions; } private static ClientOptions extractClientOptions(SimpleRequestOptions simpleOptions) { SSLContext sslContext = simpleOptions.getSslContext(); String sslCert = simpleOptions.getSslCert(); String sslKey = simpleOptions.getSslKey(); String sslCaCert = simpleOptions.getSslCaCert(); String[] sslProtocols = simpleOptions.getSslProtocols(); String[] sslCipherSuites = simpleOptions.getSslCipherSuites(); boolean insecure = simpleOptions.getInsecure(); boolean forceRedirects = simpleOptions.getForceRedirects(); boolean followRedirects = simpleOptions.getFollowRedirects(); int connectTimeoutMilliseconds = simpleOptions.getConnectTimeoutMilliseconds(); int socketTimeoutMilliseconds = simpleOptions.getSocketTimeoutMilliseconds(); return new ClientOptions(sslContext, sslCert, sslKey, sslCaCert, sslProtocols, sslCipherSuites, insecure, forceRedirects, followRedirects, connectTimeoutMilliseconds, socketTimeoutMilliseconds, // this option set is only used for one-shot requests, so at the point of being explicit, // only allow at most one connection at a time. 1, 1); } private static Response request(SimpleRequestOptions simpleRequestOptions, HttpMethod method) { // TODO: if we end up implementing an async version of the java API, // we should refactor this implementation so that it is based on the // async one, as Patrick has done in the clojure API. final Response response; final SyncHttpClient client = createClient( extractClientOptions(simpleRequestOptions)); try { response = client.request( extractRequestOptions(simpleRequestOptions), method); } finally { try { client.close(); } catch (IOException e) { LOGGER.error("Error closing client", e); } } return response; } /** * Creates a synchronous persistent HTTP client * @param clientOptions * @return A persistent synchronous HTTP client */ public static SyncHttpClient createClient(ClientOptions clientOptions) { final String metricNamespace = Metrics.buildMetricNamespace(clientOptions.getMetricPrefix(), clientOptions.getServerId()); return new PersistentSyncHttpClient(JavaClient.createClient(clientOptions), clientOptions.getMetricRegistry(), metricNamespace, clientOptions.isEnableURLMetrics()); } /** * Makes a simple HTTP GET request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response get(String url) throws URISyntaxException { return get(new URI(url)); } /** * Makes a simple HTTP GET request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response get(URI uri) { return get(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP GET request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response get(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.GET); } /** * Makes a simple HTTP HEAD request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response head(String url) throws URISyntaxException { return head(new URI(url)); } /** * Makes a simple HTTP HEAD request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response head(URI uri) { return head(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP HEAD request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response head(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.HEAD); } /** * Makes a simple HTTP POST request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response post(String url) throws URISyntaxException { return post(new URI(url)); } /** * Makes a simple HTTP POST request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response post(URI uri) { return post(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP POST request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response post(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.POST); } /** * Makes a simple HTTP PUT request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response put(String url) throws URISyntaxException { return put(new URI(url)); } /** * Makes a simple HTTP PUT request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response put(URI uri) { return put(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP PUT request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response put(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.PUT); } /** * Makes a simple HTTP DELETE request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response delete(String url) throws URISyntaxException { return delete(new URI(url)); } /** * Makes a simple HTTP DELETE request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response delete(URI uri) { return delete(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP DELETE request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response delete(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.DELETE); } /** * Makes a simple HTTP TRACE request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response trace(String url) throws URISyntaxException { return trace(new URI(url)); } /** * Makes a simple HTTP TRACE request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response trace(URI uri) { return trace(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP TRACE request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response trace(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.TRACE); } /** * Makes a simple HTTP OPTIONS request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response options(String url) throws URISyntaxException { return options(new URI(url)); } /** * Makes a simple HTTP OPTIONS request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response options(URI uri) { return options(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP OPTIONS request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response options(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.OPTIONS); } /** * Makes a simple HTTP PATCH request * @param url The URL against which to make the request * @return The HTTP Response corresponding to the request * @throws URISyntaxException */ public static Response patch(String url) throws URISyntaxException { return patch(new URI(url)); } /** * Makes a simple HTTP PATCH request * @param uri The URI against which to make the request * @return The HTTP Response corresponding to the request */ public static Response patch(URI uri) { return patch(new SimpleRequestOptions(uri)); } /** * Makes a simple HTTP PATCH request * @param simpleRequestOptions The options to configure the request and the client making it * @return The HTTP response corresponding to the request */ public static Response patch(SimpleRequestOptions simpleRequestOptions) { return request(simpleRequestOptions, HttpMethod.PATCH); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/SyncHttpClient.java000066400000000000000000000136211373223535200272030ustar00rootroot00000000000000package com.puppetlabs.http.client; import com.codahale.metrics.MetricRegistry; import java.io.Closeable; import java.net.URI; import java.net.URISyntaxException; /** * This interface represents a synchronous HTTP client with which * requests can be made. An object implementing this interface is * returned by {@link com.puppetlabs.http.client.Sync#createClient(ClientOptions)} */ public interface SyncHttpClient extends Closeable { /** * @return the MetricRegistry instance associated with this Client */ public MetricRegistry getMetricRegistry(); /** * @return the String metricNamespace for this Client */ public String getMetricNamespace(); /** * Makes a configurable HTTP request * @param requestOptions the options to configure the request * @param method the type of the HTTP request * @return the HTTP response */ public Response request(RequestOptions requestOptions, HttpMethod method); /** * Makes an HTTP GET request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response get(String url) throws URISyntaxException; /** * Makes an HTTP GET request * @param uri the uri against which to make the request * @return the HTTP response */ public Response get(URI uri); /** * Makes an HTTP GET request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response get(RequestOptions requestOptions); /** * Makes an HTTP HEAD request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response head(String url) throws URISyntaxException; /** * Makes an HTTP HEAD request * @param uri the uri against which to make the request * @return the HTTP response */ public Response head(URI uri); /** * Makes an HTTP HEAD request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response head(RequestOptions requestOptions); /** * Makes an HTTP POST request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response post(String url) throws URISyntaxException; /** * Makes an HTTP POST request * @param uri the uri against which to make the request * @return the HTTP response */ public Response post(URI uri); /** * Makes an HTTP POST request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response post(RequestOptions requestOptions); /** * Makes an HTTP PUT request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response put(String url) throws URISyntaxException; /** * Makes an HTTP PUT request * @param uri the uri against which to make the request * @return the HTTP response */ public Response put(URI uri); /** * Makes an HTTP PUT request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response put(RequestOptions requestOptions); /** * Makes an HTTP DELETE request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response delete(String url) throws URISyntaxException; /** * Makes an HTTP DELETE request * @param uri the uri against which to make the request * @return the HTTP response */ public Response delete(URI uri); /** * Makes an HTTP DELETE request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response delete(RequestOptions requestOptions); /** * Makes an HTTP TRACE request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response trace(String url) throws URISyntaxException; /** * Makes an HTTP TRACE request * @param uri the uri against which to make the request * @return the HTTP response */ public Response trace(URI uri); /** * Makes an HTTP TRACE request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response trace(RequestOptions requestOptions); /** * Makes an HTTP OPTIONS request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response options(String url) throws URISyntaxException; /** * Makes an HTTP OPTIONS request * @param uri the uri against which to make the request * @return the HTTP response */ public Response options(URI uri); /** * Makes an HTTP OPTIONS request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response options(RequestOptions requestOptions); /** * Makes an HTTP PATCH request * @param url the url against which to make the request * @return the HTTP response * @throws URISyntaxException */ public Response patch(String url) throws URISyntaxException; /** * Makes an HTTP PATCH request * @param uri the uri against which to make the request * @return the HTTP response */ public Response patch(URI uri); /** * Makes an HTTP PATCH request * @param requestOptions the options to configure the request * @return the HTTP response */ public Response patch(RequestOptions requestOptions); } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/000077500000000000000000000000001373223535200243635ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/CoercedClientOptions.java000066400000000000000000000033011373223535200313020ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import javax.net.ssl.SSLContext; public class CoercedClientOptions { private final SSLContext sslContext; private final String[] sslProtocols; private final String[] sslCipherSuites; private final boolean forceRedirects; private final boolean followRedirects; private final int connectTimeoutMilliseconds; private final int socketTimeoutMilliseconds; public CoercedClientOptions(SSLContext sslContext, String[] sslProtocols, String[] sslCipherSuites, boolean forceRedirects, boolean followRedirects, int connectTimeoutMilliseconds, int socketTimeoutMilliseconds) { this.sslContext = sslContext; this.sslProtocols = sslProtocols; this.sslCipherSuites = sslCipherSuites; this.forceRedirects = forceRedirects; this.followRedirects = followRedirects; this.connectTimeoutMilliseconds = connectTimeoutMilliseconds; this.socketTimeoutMilliseconds = socketTimeoutMilliseconds; } public SSLContext getSslContext() { return sslContext; } public String[] getSslProtocols() { return sslProtocols; } public String[] getSslCipherSuites() { return sslCipherSuites; } public boolean getForceRedirects() { return forceRedirects; } public boolean getFollowRedirects() { return followRedirects; } public int getConnectTimeoutMilliseconds() { return connectTimeoutMilliseconds; } public int getSocketTimeoutMilliseconds() { return socketTimeoutMilliseconds; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/CoercedRequestOptions.java000066400000000000000000000025551373223535200315260ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.puppetlabs.http.client.HttpMethod; import org.apache.http.Header; import org.apache.http.HttpEntity; import java.net.URI; import java.util.zip.GZIPOutputStream; class CoercedRequestOptions { private final URI uri; private final HttpMethod method; private final Header[] headers; private final HttpEntity body; private final GZIPOutputStream gzipOutputStream; private final byte[] bytesToGzip; public CoercedRequestOptions(URI uri, HttpMethod method, Header[] headers, HttpEntity body, GZIPOutputStream gzipOutputStream, byte[] bytesToGzip) { this.uri = uri; this.method = method; this.headers = headers; this.body = body; this.gzipOutputStream = gzipOutputStream; this.bytesToGzip = bytesToGzip; } public URI getUri() { return uri; } public HttpMethod getMethod() { return method; } public Header[] getHeaders() { return headers; } public HttpEntity getBody() { return body; } public GZIPOutputStream getGzipOutputStream() { return gzipOutputStream; }; public byte[] getBytesToGzip() { return bytesToGzip; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/Deliverable.java000066400000000000000000000001451373223535200274440ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; public interface Deliverable { void deliver(T t); } ExceptionInsertingPipedInputStream.java000066400000000000000000000023701373223535200341500ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/implpackage com.puppetlabs.http.client.impl; import java.io.IOException; import java.io.PipedInputStream; public class ExceptionInsertingPipedInputStream extends PipedInputStream { private final Promise ioExceptionPromise; public ExceptionInsertingPipedInputStream(Promise ioExceptionPromise) { this.ioExceptionPromise = ioExceptionPromise; } private void checkFinalResult() throws IOException { try { IOException ioException = ioExceptionPromise.deref(); if (ioException != null) { throw ioException; } } catch (InterruptedException e) { throw new RuntimeException(e); } } @Override public synchronized int read() throws IOException { int read = super.read(); if (read == -1) { checkFinalResult(); } return read; } @Override public synchronized int read(byte[] b, int off, int len) throws IOException { int read = super.read(b, off, len); if (read == -1) { checkFinalResult(); } return read; } @Override public void close() throws IOException { super.close(); checkFinalResult(); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/FnDeliverable.java000066400000000000000000000004451373223535200277330ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import clojure.lang.IFn; public class FnDeliverable implements Deliverable { private final IFn fn; public FnDeliverable(IFn fn) { this.fn = fn; } @Override public void deliver(T t) { fn.invoke(t); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/IResponseCallback.java000066400000000000000000000002561373223535200305550ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.puppetlabs.http.client.Response; public interface IResponseCallback { Response handleResponse(Response response); } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/JavaClient.java000066400000000000000000000745601373223535200272620ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.codahale.metrics.MetricRegistry; import com.puppetlabs.http.client.ClientOptions; import com.puppetlabs.http.client.CompressType; import com.puppetlabs.http.client.HttpClientException; import com.puppetlabs.http.client.HttpMethod; import com.puppetlabs.http.client.RequestOptions; import com.puppetlabs.http.client.ResponseBodyType; import com.puppetlabs.http.client.impl.metrics.TimerUtils; import com.puppetlabs.ssl_utils.SSLUtils; import org.apache.commons.io.IOUtils; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.ParseException; import org.apache.http.ProtocolException; import org.apache.http.client.RedirectStrategy; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpOptions; import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpTrace; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.ResponseContentEncoding; import org.apache.http.concurrent.FutureCallback; import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.entity.ContentType; import org.apache.http.entity.InputStreamEntity; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.client.LaxRedirectStrategy; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.message.BasicHeader; import org.apache.http.nio.client.methods.HttpAsyncMethods; import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; import org.apache.http.nio.entity.NStringEntity; import org.apache.http.protocol.HttpContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.io.InputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.nio.charset.UnsupportedCharsetException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import java.util.zip.GZIPOutputStream; public class JavaClient { private static final String PROTOCOL = "TLS"; private static final Logger LOGGER = LoggerFactory.getLogger(JavaClient.class); // Buffer size to use in streams for request gzip compression. This is // somewhat arbitrary but went with the same value as the Apache HTTP // async client uses for chunking input streams for requests: // https://github.com/apache/httpcore/blob/4.4.5/httpcore-nio/src/main/java/org/apache/http/nio/entity/EntityAsyncContentProducer.java#L58 private static int GZIP_BUFFER_SIZE = 4096; private static Header[] prepareHeaders(RequestOptions options, ContentType contentType) { Map result = new HashMap(); Map origHeaders = options.getHeaders(); if (origHeaders == null) { origHeaders = new HashMap(); } for (Map.Entry entry : origHeaders.entrySet()) { result.put(entry.getKey().toLowerCase(), new BasicHeader(entry.getKey(), entry.getValue())); } if (options.getDecompressBody() && (! result.containsKey("accept-encoding"))) { result.put("accept-encoding", new BasicHeader("Accept-Encoding", "gzip, deflate")); } if (options.getCompressRequestBody() == CompressType.GZIP && (! result.containsKey("content-encoding"))) { result.put("content-encoding", new BasicHeader("Content-Encoding", "gzip")); } if (contentType != null) { result.put("content-type", new BasicHeader("Content-Type", contentType.toString())); } return result.values().toArray(new Header[result.size()]); } public static ContentType getContentType (Object body, RequestOptions options) { ContentType contentType = null; Map headers = options.getHeaders(); if (headers != null) { for (Map.Entry entry : headers.entrySet()) { if (entry.getKey().toLowerCase().equals("content-type")) { String contentTypeValue = entry.getValue(); if (contentTypeValue != null && !contentTypeValue.isEmpty()) { try { contentType = ContentType.parse(contentTypeValue); } catch (ParseException e) { throw new HttpClientException("Unable to parse request content type", e); } catch (UnsupportedCharsetException e) { throw new HttpClientException("Unsupported content type charset", e); } // In the case when the caller provides the body as a string, and does not // specify a charset, we choose one for them. There will always be _some_ // charset used to encode the string, and in this case we choose UTF-8 // (instead of letting the underlying Apache HTTP client library // choose ISO-8859-1) because UTF-8 is a more reasonable default. if (contentType.getCharset() == null && body instanceof String) { contentType = ContentType.create(contentType.getMimeType(), Consts.UTF_8); } } } } } return contentType; } private static void throwUnsupportedBodyException(Object body) { throw new HttpClientException("Unsupported body type for request: " + body.getClass() + ". Only InputStream and String are supported."); } private static CoercedRequestOptions coerceRequestOptions(RequestOptions options, HttpMethod method) { URI uri = options.getUri(); if (method == null) { method = HttpMethod.GET; } ContentType contentType = getContentType(options.getBody(), options); Header[] headers = prepareHeaders(options, contentType); HttpEntity body = null; GZIPOutputStream gzipOutputStream = null; Object bodyFromOptions = options.getBody(); byte[] bytesToGzip = null; if ((bodyFromOptions instanceof String) || (bodyFromOptions instanceof InputStream)) { // See comments in the requestWithClient() method about why the // request body is routed through a GZIPOutputStream, // PipedOutputStream, and PipedInputStream in order to achieve // gzip compression. if (options.getCompressRequestBody() == CompressType.GZIP) { PipedInputStream pis = new PipedInputStream(GZIP_BUFFER_SIZE); PipedOutputStream pos = new PipedOutputStream(); try { pos.connect(pis); gzipOutputStream = new GZIPOutputStream(pos, GZIP_BUFFER_SIZE); body = new InputStreamEntity(pis); } catch (IOException ioe) { throw new HttpClientException( "Error setting up gzip stream for request", ioe); } if (bodyFromOptions instanceof String) { String bodyAsString = (String) bodyFromOptions; if (contentType != null) { bytesToGzip = bodyAsString.getBytes(contentType.getCharset()); } else { bytesToGzip = bodyAsString.getBytes(); } } } else if (bodyFromOptions instanceof String) { String originalBody = (String) bodyFromOptions; if (contentType != null) { body = new NStringEntity(originalBody, contentType); } else { try { body = new NStringEntity(originalBody); } catch (UnsupportedEncodingException e) { throw new HttpClientException( "Unable to create request body", e); } } } else { body = new InputStreamEntity((InputStream) bodyFromOptions); } } else if (bodyFromOptions != null) { throwUnsupportedBodyException(bodyFromOptions); } return new CoercedRequestOptions(uri, method, headers, body, gzipOutputStream, bytesToGzip); } public static CoercedClientOptions coerceClientOptions(ClientOptions options) { final SSLContext sslContext; if (options.getSslContext() != null) { sslContext = options.getSslContext(); } else if (options.getInsecure()) { sslContext = getInsecureSslContext(); } else { sslContext = null; } final String[] sslProtocols; if (options.getSslProtocols() != null) { sslProtocols = options.getSslProtocols(); } else { sslProtocols = ClientOptions.DEFAULT_SSL_PROTOCOLS; } final String[] sslCipherSuites; if (options.getSslCipherSuites() != null) { sslCipherSuites = options.getSslCipherSuites(); } else { sslCipherSuites = null; } boolean forceRedirects = options.getForceRedirects(); boolean followRedirects = options.getFollowRedirects(); int connectTimeoutMilliseconds = options.getConnectTimeoutMilliseconds(); int socketTimeoutMilliseconds = options.getSocketTimeoutMilliseconds(); return new CoercedClientOptions(sslContext, sslProtocols, sslCipherSuites, forceRedirects, followRedirects, connectTimeoutMilliseconds, socketTimeoutMilliseconds); } private static SSLContext getInsecureSslContext() { final SSLContext context; try { if (SSLUtils.isFIPS()) { context = SSLContext.getInstance(SSLUtils.TLS_PROTOCOL, SSLUtils.BOUNCYCASTLE_JSSE_PROVIDER); } else { context = SSLContext.getInstance(PROTOCOL); } } catch (NoSuchAlgorithmException | NoSuchProviderException e) { throw new HttpClientException("Unable to construct HTTP context", e); } try { context.init(null, new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { // Always trust } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // Always trust } }}, null); } catch (KeyManagementException e) { throw new HttpClientException("Unable to initialize insecure SSL context", e); } return context; } private static void completeResponse(ResponseDeliveryDelegate responseDeliveryDelegate, RequestOptions requestOptions, IResponseCallback callback, HttpResponse httpResponse, HttpContext httpContext) { try { Map headers = new HashMap<>(); for (Header h : httpResponse.getAllHeaders()) { headers.put(h.getName().toLowerCase(), h.getValue()); } String origContentEncoding = headers.get("content-encoding"); if (requestOptions.getDecompressBody()) { new ResponseContentEncoding().process(httpResponse, httpContext); } Object body = null; HttpEntity entity = httpResponse.getEntity(); if (entity != null) { body = entity.getContent(); } ContentType contentType = null; String contentTypeValue = headers.get("content-type"); if (contentTypeValue != null && !contentTypeValue.isEmpty()) { try { contentType = ContentType.parse(contentTypeValue); } catch (ParseException e) { LOGGER.error("Unable to parse response content-type", e); } } if (requestOptions.getAs() == ResponseBodyType.TEXT) { body = coerceBodyType((InputStream) body, requestOptions.getAs(), contentType); } StatusLine status = httpResponse.getStatusLine(); responseDeliveryDelegate.deliverResponse(requestOptions, origContentEncoding, body, headers, status.getStatusCode(), status.getReasonPhrase(), contentType, callback); } catch (Exception e) { responseDeliveryDelegate.deliverResponse(requestOptions, e, callback); } } private static void executeWithConsumer(final CloseableHttpAsyncClient client, final FutureCallback futureCallback, final HttpRequestBase request, final MetricRegistry metricRegistry, final String[] metricId, final String metricNamespace, final boolean enableURLMetrics) { /* * Create an Apache AsyncResponseConsumer that will return the response to us as soon as it is available, * then send the response body asynchronously */ final StreamingAsyncResponseConsumer consumer = new StreamingAsyncResponseConsumer(new Deliverable() { @Override public void deliver(HttpResponse httpResponse) { futureCallback.completed(httpResponse); } }); /* * Normally the consumer returns the response as soon as it is available using the deliver() callback (above) * which delegates to the supplied futureCallback. * * If an error occurs early in the request, the consumer may not get a chance to deliver the response. This * streamingCompleteCallback wraps the supplied futureCallback and ensures: * - The supplied futureCallback is always eventually called even in error states * - Any exception that occurs during stream processing (after the response has been returned) is propagated * back to the client using the setFinalResult() method. */ final FutureCallback streamingCompleteCallback = new FutureCallback() { @Override public void completed(HttpResponse httpResponse) { consumer.setFinalResult(null); futureCallback.completed(httpResponse); } @Override public void failed(Exception e) { if (e instanceof IOException) { consumer.setFinalResult((IOException) e); } else { consumer.setFinalResult(new IOException(e)); } futureCallback.failed(e); } @Override public void cancelled() { consumer.setFinalResult(null); futureCallback.cancelled(); } }; TimedFutureCallback timedStreamingCompleteCallback = new TimedFutureCallback<>(streamingCompleteCallback, TimerUtils.startFullResponseTimers(metricRegistry, request, metricId, metricNamespace, enableURLMetrics)); client.execute(HttpAsyncMethods.create(request), consumer, timedStreamingCompleteCallback); } private static void gzipRequestPayload( GZIPOutputStream gzipOutputStream, byte[] bytesToGzip, Object requestBody) { try { if (bytesToGzip != null) { gzipOutputStream.write(bytesToGzip); } else { if (requestBody instanceof InputStream) { InputStream requestInputStream = (InputStream) requestBody; byte[] byteBuffer = new byte[GZIP_BUFFER_SIZE]; try { IOUtils.copyLarge(requestInputStream, gzipOutputStream, byteBuffer); } finally { requestInputStream.close(); } } else { throwUnsupportedBodyException(requestBody); } } // IOExceptions may be thrown either during the IOUtils.copyLarge() // call above or during the close() call to the GZIPOutputStream below. // The GZIPOutputStream object is backed by a PipedOutputStream object // which is connected to a PipedInputStream object. Most likely, any // IOExceptions thrown up to this level would be due to the underlying // PipedInputStream being closed prematurely. In those cases, the // Apache HTTP Async library should detect the failure while processing // the request and deliver an appropriate "failure" callback as the // result for the request. The IOExceptions are caught and not rethrown // here so that the client can still receive the "failure" callback from // the Apache HTTP Async library later on. The exceptions are still // logged here at a debug level for troubleshooting purposes. } catch (IOException ioe) { LOGGER.debug("Error writing gzip request body", ioe); } finally { try { gzipOutputStream.close(); } catch (IOException ioe) { LOGGER.debug("Error closing gzip request stream", ioe); } } } public static void requestWithClient(final RequestOptions requestOptions, final HttpMethod method, final IResponseCallback callback, final CloseableHttpAsyncClient client, final ResponseDeliveryDelegate responseDeliveryDelegate, final MetricRegistry registry, final String metricNamespace, final boolean enableURLMetrics) { final CoercedRequestOptions coercedRequestOptions = coerceRequestOptions(requestOptions, method); final HttpRequestBase request = constructRequest(coercedRequestOptions.getMethod(), coercedRequestOptions.getUri(), coercedRequestOptions.getBody()); request.setHeaders(coercedRequestOptions.getHeaders()); final HttpContext httpContext = HttpClientContext.create(); final FutureCallback futureCallback = new FutureCallback() { @Override public void completed(HttpResponse httpResponse) { completeResponse(responseDeliveryDelegate, requestOptions, callback, httpResponse, httpContext); } @Override public void failed(Exception e) { responseDeliveryDelegate.deliverResponse(requestOptions, e, callback); } @Override public void cancelled() { responseDeliveryDelegate.deliverResponse(requestOptions, new HttpClientException("Request cancelled"), callback); } }; final String[] metricId = requestOptions.getMetricId(); if (requestOptions.getAs() == ResponseBodyType.UNBUFFERED_STREAM) { executeWithConsumer(client, futureCallback, request, registry, metricId, metricNamespace, enableURLMetrics); } else { TimedFutureCallback timedFutureCallback = new TimedFutureCallback<>(futureCallback, TimerUtils.startFullResponseTimers(registry, request, metricId, metricNamespace, enableURLMetrics)); client.execute(request, timedFutureCallback); } // The approach used for gzip-compressing the request payload is far from // ideal here. The approach involves reading the bytes from the supplied // request body, redirecting those through a JDK GZIPOutputStream object // to compress them, and then piping those back into in InputStream that // the Apache Async HTTP layer reads from in order to get the bytes // to transmit in the HTTP request. The JDK apparently has no built-in // functionality for gzip-compressing a source byte array or InputStream // back into a separate InputStream that the Apache Async HTTP layer // could use. // // A better approach would probably be to do something like one of the // approaches discussed in http://stackoverflow.com/questions/11036280/compress-an-inputstream-with-gzip. // For example, the InputStream given to the Apache Async HTTP layer // could be wrapped with a FilterInputStream which gzip-compresses // bytes on the fly as the Apache Async HTTP layer asks for them. The // approaches on that thread are pretty involved, though, and appear to // have liberally copied content from the JDK source. A clean-room // implementation would probably be better but would also likely // require a fair bit of testing to ensure that it produces good gzip // content under varying read scenarios. // // The approach being used for now requires writing through the // GZIPOutputStream and underlying PipedOutputStream from the thread on // which the HTTP request is made. The connected PipedInputStream is // then read from a separate thread, one of the Apache HTTP Async IO // worker threads -- hopefully avoiding the possibility of a deadlock // in the process. // // For requests that provide an InputStream as a source argument, it // would also probably be more performant to do the GZIPOutputStream // writing from a separate thread and would give an AsyncHttpClient // requestor the ability to do other work while the source InputStream // is being read and compressed. As a simplification for now, this // implementation doesn't spin up a separate thread (or thread pool) // for performing gzip compression. GZIPOutputStream gzipOutputStream = coercedRequestOptions.getGzipOutputStream(); if (gzipOutputStream != null) { gzipRequestPayload(gzipOutputStream, coercedRequestOptions.getBytesToGzip(), requestOptions.getBody()); } } public static CloseableHttpAsyncClient createClient(ClientOptions clientOptions) { CoercedClientOptions coercedOptions = coerceClientOptions(SslUtils.configureSsl(clientOptions)); HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom(); clientBuilder.setMaxConnPerRoute(clientOptions.getMaxConnectionsPerRoute()); clientBuilder.setMaxConnTotal(clientOptions.getMaxConnectionsTotal()); SSLContext context = coercedOptions.getSslContext(); if (context != null) { // this requires an initialized SSLContext clientBuilder.setSSLStrategy( new SSLIOSessionStrategy(context, coercedOptions.getSslProtocols(), coercedOptions.getSslCipherSuites(), new DefaultHostnameVerifier())); } RedirectStrategy redirectStrategy; if (!coercedOptions.getFollowRedirects()) { redirectStrategy = new RedirectStrategy() { @Override public boolean isRedirected(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) throws ProtocolException { return false; } @Override public HttpUriRequest getRedirect(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) throws ProtocolException { return null; } }; } else if (coercedOptions.getForceRedirects()) { redirectStrategy = new LaxRedirectStrategy(); } else { redirectStrategy = new DefaultRedirectStrategy(); } clientBuilder.setRedirectStrategy(redirectStrategy); RequestConfig requestConfig = getRequestConfig(coercedOptions); if (requestConfig != null) { clientBuilder.setDefaultRequestConfig(requestConfig); } CloseableHttpAsyncClient client = clientBuilder.build(); client.start(); return client; } private static RequestConfig getRequestConfig (CoercedClientOptions options) { RequestConfig config = null; int connectTimeoutMilliseconds = options.getConnectTimeoutMilliseconds(); int socketTimeoutMilliseconds = options.getSocketTimeoutMilliseconds(); if (connectTimeoutMilliseconds >= 0 || socketTimeoutMilliseconds >= 0) { Builder requestConfigBuilder = RequestConfig.custom(); if (connectTimeoutMilliseconds >= 0) { requestConfigBuilder.setConnectTimeout (connectTimeoutMilliseconds); } if (socketTimeoutMilliseconds >= 0) { requestConfigBuilder.setSocketTimeout (socketTimeoutMilliseconds); } config = requestConfigBuilder.build(); } return config; } private static HttpRequestBase constructRequest(HttpMethod httpMethod, URI uri, HttpEntity body) { switch (httpMethod) { case GET: return requestWithNoBody(new HttpGet(uri), body, httpMethod); case HEAD: return requestWithNoBody(new HttpHead(uri), body, httpMethod); case POST: return requestWithBody(new HttpPost(uri), body); case PUT: return requestWithBody(new HttpPut(uri), body); case DELETE: return requestWithNoBody(new HttpDelete(uri), body, httpMethod); case TRACE: return requestWithNoBody(new HttpTrace(uri), body, httpMethod); case OPTIONS: return requestWithNoBody(new HttpOptions(uri), body, httpMethod); case PATCH: return requestWithBody(new HttpPatch(uri), body); default: throw new HttpClientException("Unable to construct request for:" + httpMethod + ", " + uri.toString(), null); } } private static HttpRequestBase requestWithBody(HttpEntityEnclosingRequestBase request, HttpEntity body) { if (body != null) { request.setEntity(body); } return request; } private static HttpRequestBase requestWithNoBody(HttpRequestBase request, Object body, HttpMethod httpMethod) { if (body != null) { throw new HttpClientException("Request of type " + httpMethod + " does not support 'body'!"); } return request; } public static Object coerceBodyType(InputStream body, ResponseBodyType as, ContentType contentType) { Object response = null; switch (as) { case TEXT: String charset = "UTF-8"; if ((contentType != null) && (contentType.getCharset() != null)) { charset = contentType.getCharset().name(); } try { if (body == null){ response = ""; } else{ response = IOUtils.toString(body, charset); } } catch (IOException e) { throw new HttpClientException("Unable to read body as string", e); } try { if (body != null){ body.close(); } } catch (IOException e) { throw new HttpClientException( "Unable to close response stream", e); } break; default: throw new HttpClientException("Unsupported body type: " + as); } return response; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/JavaResponseDeliveryDelegate.java000066400000000000000000000035551373223535200327750ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.puppetlabs.http.client.RequestOptions; import com.puppetlabs.http.client.Response; import org.apache.http.entity.ContentType; import java.util.Map; public final class JavaResponseDeliveryDelegate implements ResponseDeliveryDelegate { private final Promise promise; public JavaResponseDeliveryDelegate(Promise promise) { this.promise = promise; } private void deliverResponse(Response response, RequestOptions requestOptions, IResponseCallback callback) { if (callback != null) { try { promise.deliver(callback.handleResponse(response)); } catch (Exception e) { promise.deliver(new Response(requestOptions, e)); } } else { promise.deliver(response); } } @Override public void deliverResponse(RequestOptions requestOptions, String origContentEncoding, Object body, Map headers, int statusCode, String reasonPhrase, ContentType contentType, IResponseCallback callback) { Response response = new Response(requestOptions, origContentEncoding, body, headers, statusCode, reasonPhrase, contentType); deliverResponse(response, requestOptions, callback); } @Override public void deliverResponse(RequestOptions requestOptions, Exception e, IResponseCallback callback) { deliverResponse(new Response(requestOptions, e), requestOptions, callback); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/PersistentAsyncHttpClient.java000066400000000000000000000107301373223535200323640ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.codahale.metrics.MetricRegistry; import com.puppetlabs.http.client.Response; import com.puppetlabs.http.client.RequestOptions; import com.puppetlabs.http.client.HttpMethod; import com.puppetlabs.http.client.AsyncHttpClient; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; public class PersistentAsyncHttpClient implements AsyncHttpClient { private CloseableHttpAsyncClient client; private MetricRegistry metricRegistry; private String metricNamespace; private boolean enableURLMetrics; public PersistentAsyncHttpClient(CloseableHttpAsyncClient client, MetricRegistry metricRegistry, String metricNamespace, boolean enableURLMetrics) { this.client = client; this.metricRegistry = metricRegistry; this.metricNamespace = metricNamespace; this.enableURLMetrics = enableURLMetrics; } public void close() throws IOException { client.close(); } public MetricRegistry getMetricRegistry() { return metricRegistry; } public String getMetricNamespace() { return metricNamespace; } private Promise request(RequestOptions requestOptions, HttpMethod method) { final Promise promise = new Promise<>(); final JavaResponseDeliveryDelegate responseDelivery = new JavaResponseDeliveryDelegate(promise); JavaClient.requestWithClient(requestOptions, method, null, client, responseDelivery, metricRegistry, metricNamespace, enableURLMetrics); return promise; } public Promise get(String url) throws URISyntaxException { return get(new URI(url)); } public Promise get(URI uri) { return get(new RequestOptions(uri)); } public Promise get(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.GET); } public Promise head(String url) throws URISyntaxException { return head(new URI(url)); } public Promise head(URI uri) { return head(new RequestOptions(uri)); } public Promise head(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.HEAD); } public Promise post(String url) throws URISyntaxException { return post(new URI(url)); } public Promise post(URI uri) { return post(new RequestOptions(uri)); } public Promise post(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.POST); } public Promise put(String url) throws URISyntaxException { return put(new URI(url)); } public Promise put(URI uri) { return put(new RequestOptions(uri)); } public Promise put(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.PUT); } public Promise delete(String url) throws URISyntaxException { return delete(new URI(url)); } public Promise delete(URI uri) { return delete(new RequestOptions(uri)); } public Promise delete(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.DELETE); } public Promise trace(String url) throws URISyntaxException { return trace(new URI(url)); } public Promise trace(URI uri) { return trace(new RequestOptions(uri)); } public Promise trace(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.TRACE); } public Promise options(String url) throws URISyntaxException { return options(new URI(url)); } public Promise options(URI uri) { return options(new RequestOptions(uri)); } public Promise options(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.OPTIONS); } public Promise patch(String url) throws URISyntaxException { return patch(new URI(url)); } public Promise patch(URI uri) { return patch(new RequestOptions(uri)); } public Promise patch(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.PATCH); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/PersistentSyncHttpClient.java000066400000000000000000000117351373223535200322310ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.codahale.metrics.MetricRegistry; import com.puppetlabs.http.client.HttpClientException; import com.puppetlabs.http.client.Response; import com.puppetlabs.http.client.RequestOptions; import com.puppetlabs.http.client.HttpMethod; import com.puppetlabs.http.client.SyncHttpClient; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; public class PersistentSyncHttpClient implements SyncHttpClient { private CloseableHttpAsyncClient client; private MetricRegistry metricRegistry; private String metricNamespace; private boolean enableURLMetrics; private static final Logger LOGGER = LoggerFactory.getLogger(PersistentSyncHttpClient.class); public PersistentSyncHttpClient(CloseableHttpAsyncClient client, MetricRegistry metricRegistry, String metricNamespace, boolean enableURLMetrics) { this.client = client; this.metricRegistry = metricRegistry; this.metricNamespace = metricNamespace; this.enableURLMetrics = enableURLMetrics; } public MetricRegistry getMetricRegistry() { return metricRegistry; } public String getMetricNamespace() { return metricNamespace; } public Response request(RequestOptions requestOptions, HttpMethod method) { final Promise promise = new Promise<>(); final JavaResponseDeliveryDelegate responseDelivery = new JavaResponseDeliveryDelegate(promise); JavaClient.requestWithClient(requestOptions, method, null, client, responseDelivery, metricRegistry, metricNamespace, enableURLMetrics); final Response response; try { response = promise.deref(); if (response.getError() != null) { LOGGER.warn("Error executing http request", response.getError()); throw new HttpClientException("Error executing http request", response.getError()); } } catch (InterruptedException e) { LOGGER.warn("Error while waiting for http response", e); throw new HttpClientException("Error while waiting for http response", e); } return response; } public void close() throws IOException { client.close(); } public Response get(String url) throws URISyntaxException { return get(new URI(url)); } public Response get(URI uri) { return get(new RequestOptions(uri)); } public Response get(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.GET); } public Response head(String url) throws URISyntaxException { return head(new URI(url)); } public Response head(URI uri) { return head(new RequestOptions(uri)); } public Response head(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.HEAD); } public Response post(String url) throws URISyntaxException { return post(new URI(url)); } public Response post(URI uri) { return post(new RequestOptions(uri)); } public Response post(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.POST); } public Response put(String url) throws URISyntaxException { return put(new URI(url)); } public Response put(URI uri) { return put(new RequestOptions(uri)); } public Response put(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.PUT); } public Response delete(String url) throws URISyntaxException { return delete(new URI(url)); } public Response delete(URI uri) { return delete(new RequestOptions(uri)); } public Response delete(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.DELETE); } public Response trace(String url) throws URISyntaxException { return trace(new URI(url)); } public Response trace(URI uri) { return trace(new RequestOptions(uri)); } public Response trace(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.TRACE); } public Response options(String url) throws URISyntaxException { return options(new URI(url)); } public Response options(URI uri) { return options(new RequestOptions(uri)); } public Response options(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.OPTIONS); } public Response patch(String url) throws URISyntaxException { return patch(new URI(url)); } public Response patch(URI uri) { return patch(new RequestOptions(uri)); } public Response patch(RequestOptions requestOptions) { return request(requestOptions, HttpMethod.PATCH); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/Promise.java000066400000000000000000000011771373223535200266520ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import java.util.concurrent.CountDownLatch; public class Promise implements Deliverable { private final CountDownLatch latch; private T value = null; public Promise() { latch = new CountDownLatch(1); } public synchronized void deliver(T t) { if (value != null) { throw new IllegalStateException("Attempting to deliver value to a promise that has already been realized!"); } value = t; latch.countDown(); } public T deref() throws InterruptedException { latch.await(); return value; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/ResponseDeliveryDelegate.java000066400000000000000000000013401373223535200321610ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.puppetlabs.http.client.RequestOptions; import org.apache.http.entity.ContentType; public interface ResponseDeliveryDelegate { void deliverResponse(RequestOptions requestOptions, String origContentEncoding, Object body, java.util.Map headers, int statusCode, String reasonPhrase, ContentType contentType, IResponseCallback callback); void deliverResponse(RequestOptions requestOptions, Exception e, IResponseCallback callback); } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/SslUtils.java000066400000000000000000000045151373223535200270150ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.puppetlabs.ssl_utils.SSLUtils; import com.puppetlabs.http.client.HttpClientException; import com.puppetlabs.http.client.ClientOptions; import com.puppetlabs.http.client.Sync; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.FileReader; import java.io.IOException; import java.security.*; import java.security.cert.CertificateException; public class SslUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SslUtils.class); public static ClientOptions configureSsl(ClientOptions options) { if (options.getSslContext() != null) { return options; } if ((options.getSslCert() != null) && (options.getSslKey() != null) && (options.getSslCaCert() != null)) { try { options.setSslContext( SSLUtils.pemsToSSLContext( new FileReader(options.getSslCert()), new FileReader(options.getSslKey()), new FileReader(options.getSslCaCert())) ); } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException | NoSuchProviderException e) { LOGGER.error("Error while configuring SSL", e); throw new HttpClientException("Error while configuring SSL", e); } options.setSslCert(null); options.setSslKey(null); options.setSslCaCert(null); return options; } if (options.getSslCaCert() != null) { try { options.setSslContext( SSLUtils.caCertPemToSSLContext( new FileReader(options.getSslCaCert())) ); } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException | KeyManagementException | NoSuchProviderException e) { LOGGER.error("Error while configuring SSL", e); throw new HttpClientException("Error while configuring SSL", e); } options.setSslCaCert(null); return options; } return options; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/StreamingAsyncResponseConsumer.java000066400000000000000000000040631373223535200334130ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import org.apache.http.HttpResponse; import org.apache.http.entity.BasicHttpEntity; import org.apache.http.nio.IOControl; import org.apache.http.nio.client.methods.AsyncByteConsumer; import org.apache.http.protocol.HttpContext; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.nio.ByteBuffer; public class StreamingAsyncResponseConsumer extends AsyncByteConsumer { private volatile HttpResponse response; private volatile PipedOutputStream pos; private volatile Deliverable promise; private volatile Promise ioExceptionPromise = new Promise<>(); public void setFinalResult(IOException ioException) { ioExceptionPromise.deliver(ioException); } public StreamingAsyncResponseConsumer(Deliverable promise) { this.promise = promise; } @Override protected void onResponseReceived(final HttpResponse response) throws IOException { PipedInputStream pis = new ExceptionInsertingPipedInputStream(ioExceptionPromise); pos = new PipedOutputStream(); pos.connect(pis); ((BasicHttpEntity) response.getEntity()).setContent(pis); this.response = response; promise.deliver(response); } @Override protected void onByteReceived(final ByteBuffer buf, final IOControl ioctrl) throws IOException { while (buf.hasRemaining()) { byte[] bs = new byte[buf.remaining()]; buf.get(bs); pos.write(bs); } } @Override protected void releaseResources() { super.releaseResources(); this.response = null; this.promise = null; try { if (pos != null) { this.pos.close(); this.pos = null; } } catch (IOException e) { throw new IllegalStateException(e); } } @Override protected HttpResponse buildResult(final HttpContext context) { return response; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/TimedFutureCallback.java000066400000000000000000000020611373223535200310770ustar00rootroot00000000000000package com.puppetlabs.http.client.impl; import com.codahale.metrics.Timer; import org.apache.http.concurrent.FutureCallback; import java.util.ArrayList; public final class TimedFutureCallback implements FutureCallback { private final FutureCallback delegate; private final ArrayList timerContexts; public TimedFutureCallback(FutureCallback delegate, ArrayList timerContexts) { this.delegate = delegate; this.timerContexts = timerContexts; } public void completed(T result) { stopTimerContexts(); delegate.completed(result); } public void failed(Exception ex) { stopTimerContexts(); delegate.failed(ex); } public void cancelled() { stopTimerContexts(); delegate.cancelled(); } private void stopTimerContexts() { if (timerContexts != null) { for (Timer.Context timerContext : timerContexts) { timerContext.stop(); } } } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/metrics/000077500000000000000000000000001373223535200260315ustar00rootroot00000000000000CategoryClientTimerMetricFilter.java000066400000000000000000000012261373223535200350450ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/metricspackage com.puppetlabs.http.client.impl.metrics; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricFilter; import com.puppetlabs.http.client.metrics.ClientTimer; import com.puppetlabs.http.client.metrics.Metrics; public class CategoryClientTimerMetricFilter implements MetricFilter { private final Metrics.MetricCategory category; public CategoryClientTimerMetricFilter(Metrics.MetricCategory category) { this.category = category; } @Override public boolean matches(String s, Metric metric) { return metric instanceof ClientTimer && ((ClientTimer) metric).isCategory(category); } } MetricIdClientTimerFilter.java000066400000000000000000000012441373223535200336240ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/metricspackage com.puppetlabs.http.client.impl.metrics; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricFilter; import com.puppetlabs.http.client.metrics.MetricIdClientTimer; import java.util.List; public class MetricIdClientTimerFilter implements MetricFilter { private final List metricId; public MetricIdClientTimerFilter(List metricId) { this.metricId = metricId; } @Override public boolean matches(String s, Metric metric) { return metric.getClass().equals(MetricIdClientTimer.class) && ((MetricIdClientTimer) metric). getMetricId().equals(metricId); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/metrics/TimerMetricData.java000066400000000000000000000023401373223535200317110ustar00rootroot00000000000000package com.puppetlabs.http.client.impl.metrics; import com.puppetlabs.http.client.metrics.ClientTimer; import java.util.concurrent.TimeUnit; public class TimerMetricData { public static TimerMetricData fromTimer(ClientTimer timer) { Double mean = timer.getSnapshot().getMean(); Long count = timer.getCount(); Long meanMillis = TimeUnit.NANOSECONDS.toMillis(mean.longValue()); return new TimerMetricData( timer.getMetricName(), meanMillis, count, count * meanMillis); } private final String metricName; private final Long meanMillis; private final Long count; private final Long aggregate; public TimerMetricData(String metricName, Long meanMillis, Long count, Long aggregate) { this.metricName = metricName; this.meanMillis = meanMillis; this.count = count; this.aggregate = aggregate; } public String getMetricName() { return metricName; } public Long getMeanMillis() { return meanMillis; } public Long getCount() { return count; } public Long getAggregate() { return aggregate; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/metrics/TimerUtils.java000066400000000000000000000136671373223535200310120ustar00rootroot00000000000000package com.puppetlabs.http.client.impl.metrics; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.puppetlabs.http.client.metrics.ClientTimer; import com.puppetlabs.http.client.metrics.MetricIdClientTimer; import com.puppetlabs.http.client.metrics.Metrics; import com.puppetlabs.http.client.metrics.UrlAndMethodClientTimer; import com.puppetlabs.http.client.metrics.UrlClientTimer; import org.apache.http.HttpRequest; import org.apache.http.RequestLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Map; public class TimerUtils { private static final Logger LOGGER = LoggerFactory.getLogger(TimerUtils.class); private static ClientTimer getOrAddTimer(MetricRegistry metricRegistry, String name, ClientTimer newTimer) { final Map metrics = metricRegistry.getMetrics(); final Metric metric = metrics.get(name); if ( metric instanceof ClientTimer ) { return (ClientTimer) metric; } else if ( metric == null ) { try { return metricRegistry.register(name, newTimer); } catch (IllegalArgumentException e) { final Metric added = metricRegistry.getMetrics().get(name); if ( added instanceof ClientTimer ) { return (ClientTimer) added; } } } throw new IllegalArgumentException(name +" is already used for a different type of metric"); } private static ArrayList startFullResponseMetricIdTimers(MetricRegistry registry, String[] metricId, String metricPrefix) { ArrayList timerContexts = new ArrayList<>(); for (int i = 0; i < metricId.length; i++) { ArrayList currentId = new ArrayList<>(); for (int j = 0; j <= i; j++) { currentId.add(metricId[j]); } ArrayList currentIdWithNamespace = new ArrayList<>(); currentIdWithNamespace.add(Metrics.NAMESPACE_METRIC_ID); currentIdWithNamespace.addAll(currentId); currentIdWithNamespace.add(Metrics.NAMESPACE_FULL_RESPONSE); String metric_name = MetricRegistry.name(metricPrefix, currentIdWithNamespace.toArray(new String[currentIdWithNamespace.size()])); ClientTimer timer = new MetricIdClientTimer(metric_name, currentId, Metrics.MetricType.FULL_RESPONSE); timerContexts.add(getOrAddTimer(registry, metric_name, timer).time()); } return timerContexts; } private static ArrayList startFullResponseUrlTimers(MetricRegistry registry, HttpRequest request, String metricPrefix, boolean enableURLMetrics) { ArrayList timerContexts = new ArrayList<>(); if (enableURLMetrics) { try { final RequestLine requestLine = request.getRequestLine(); final String strippedUrl = Metrics.urlToMetricUrl(requestLine.getUri()); final String method = requestLine.getMethod(); final String urlName = MetricRegistry.name(metricPrefix, Metrics.NAMESPACE_URL, strippedUrl, Metrics.NAMESPACE_FULL_RESPONSE); final String urlAndMethodName = MetricRegistry.name(metricPrefix, Metrics.NAMESPACE_URL_AND_METHOD, strippedUrl, method, Metrics.NAMESPACE_FULL_RESPONSE); ClientTimer urlTimer = new UrlClientTimer(urlName, strippedUrl, Metrics.MetricType.FULL_RESPONSE); timerContexts.add(getOrAddTimer(registry, urlName, urlTimer).time()); ClientTimer urlMethodTimer = new UrlAndMethodClientTimer(urlAndMethodName, strippedUrl, method, Metrics.MetricType.FULL_RESPONSE); timerContexts.add(getOrAddTimer(registry, urlAndMethodName, urlMethodTimer).time()); } catch (URISyntaxException e) { // this shouldn't be possible LOGGER.warn("Could not build URI out of the request URI. Will not create URI timers. " + "We recommend you read http://www.stilldrinking.com/programming-sucks. " + "'now all your snowflakes are urine and you can't even find the cat.'"); } } return timerContexts; } public static ArrayList startFullResponseTimers(MetricRegistry clientRegistry, HttpRequest request, String[] metricId, String metricNamespace, boolean enableURLMetrics) { if (clientRegistry != null) { ArrayList urlTimerContexts = startFullResponseUrlTimers(clientRegistry, request, metricNamespace, enableURLMetrics); ArrayList allTimerContexts = new ArrayList<>(urlTimerContexts); if (metricId != null) { ArrayList metricIdTimers = startFullResponseMetricIdTimers(clientRegistry, metricId, metricNamespace); allTimerContexts.addAll(metricIdTimers); } return allTimerContexts; } else { return null; } } } UrlAndMethodClientTimerFilter.java000066400000000000000000000013741373223535200344560ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/metricspackage com.puppetlabs.http.client.impl.metrics; import com.codahale.metrics.Metric; import com.puppetlabs.http.client.metrics.UrlAndMethodClientTimer; public class UrlAndMethodClientTimerFilter extends UrlClientTimerFilter { private final String method; public UrlAndMethodClientTimerFilter(String url, String method) { super(url); this.method = method; } @Override public boolean matches(String s, Metric metric) { if (metric.getClass().equals(UrlAndMethodClientTimer.class)) { UrlAndMethodClientTimer timer = (UrlAndMethodClientTimer) metric; return timer.getMethod().equals(this.method) && timer.getUrl().equals(this.getUrl()); } return false; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/impl/metrics/UrlClientTimerFilter.java000066400000000000000000000012041373223535200327410ustar00rootroot00000000000000package com.puppetlabs.http.client.impl.metrics; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricFilter; import com.puppetlabs.http.client.metrics.UrlClientTimer; public class UrlClientTimerFilter implements MetricFilter { private final String url; public UrlClientTimerFilter(String url) { this.url = url; } protected String getUrl() { return url; } @Override public boolean matches(String s, Metric metric) { return metric.getClass().equals(UrlClientTimer.class) && ((UrlClientTimer) metric). getUrl().equals(url); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/000077500000000000000000000000001373223535200250705ustar00rootroot00000000000000clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/ClientMetricData.java000066400000000000000000000012201373223535200311020ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import com.puppetlabs.http.client.impl.metrics.TimerMetricData; public abstract class ClientMetricData { private final TimerMetricData timerMetricData; ClientMetricData(TimerMetricData timerMetricData) { this.timerMetricData = timerMetricData; } public String getMetricName() { return timerMetricData.getMetricName(); } public Long getCount() { return timerMetricData.getCount(); } public Long getMean() { return timerMetricData.getMeanMillis(); } public Long getAggregate() { return timerMetricData.getAggregate(); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/ClientMetricDataContainer.java000066400000000000000000000017131373223535200327540ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import java.util.List; public class ClientMetricDataContainer { private final List urlData; private final List urlAndMethodData; private final List metricIdData; public ClientMetricDataContainer(List urlTimers, List urlAndMethodData, List metricIdData) { this.urlData = urlTimers; this.urlAndMethodData = urlAndMethodData; this.metricIdData = metricIdData; } public List getUrlData() { return urlData; } public List getUrlAndMethodData() { return urlAndMethodData; } public List getMetricIdData() { return metricIdData; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/ClientTimer.java000066400000000000000000000011471373223535200301550ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import com.codahale.metrics.Timer; public abstract class ClientTimer extends Timer { private final String metricName; private final Metrics.MetricType metricType; ClientTimer(String metricName, Metrics.MetricType metricType) { super(); this.metricName = metricName; this.metricType = metricType; } public String getMetricName() { return metricName; } public Metrics.MetricType getMetricType() { return metricType; } public abstract boolean isCategory(Metrics.MetricCategory category); } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/ClientTimerContainer.java000066400000000000000000000016521373223535200320210ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import java.util.List; public class ClientTimerContainer { private final List urlTimers; private final List urlAndMethodTimers; private final List metricIdTimers; public ClientTimerContainer(List urlTimers, List urlAndMethodTimers, List metricIdTimers) { this.urlTimers = urlTimers; this.urlAndMethodTimers = urlAndMethodTimers; this.metricIdTimers = metricIdTimers; } public List getUrlTimers() { return urlTimers; } public List getUrlAndMethodTimers() { return urlAndMethodTimers; } public List getMetricIdTimers() { return metricIdTimers; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/MetricIdClientMetricData.java000066400000000000000000000010051373223535200325240ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import com.puppetlabs.http.client.impl.metrics.TimerMetricData; import java.util.List; public class MetricIdClientMetricData extends ClientMetricData { private final List metricId; public MetricIdClientMetricData(TimerMetricData timerMetricData, List metricId) { super(timerMetricData); this.metricId = metricId; } public List getMetricId() { return metricId; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/MetricIdClientTimer.java000066400000000000000000000011311373223535200315670ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import java.util.List; public class MetricIdClientTimer extends ClientTimer { private final List metricId; public MetricIdClientTimer(String metricName, List metricId, Metrics.MetricType metricType) { super(metricName, metricType); this.metricId = metricId; } public List getMetricId() { return metricId; } @Override public boolean isCategory(Metrics.MetricCategory category) { return category.equals(Metrics.MetricCategory.METRIC_ID); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/Metrics.java000066400000000000000000000237151373223535200273510ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import com.codahale.metrics.MetricFilter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.puppetlabs.http.client.impl.metrics.CategoryClientTimerMetricFilter; import com.puppetlabs.http.client.impl.metrics.MetricIdClientTimerFilter; import com.puppetlabs.http.client.impl.metrics.TimerMetricData; import com.puppetlabs.http.client.impl.metrics.UrlAndMethodClientTimerFilter; import com.puppetlabs.http.client.impl.metrics.UrlClientTimerFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; public class Metrics { public static final String PUPPETLABS_NAMESPACE_PREFIX = "puppetlabs"; public static final String HTTP_CLIENT_NAMESPACE_PREFIX = "http-client.experimental"; public static final String DEFAULT_NAMESPACE_PREFIX = PUPPETLABS_NAMESPACE_PREFIX + "." + HTTP_CLIENT_NAMESPACE_PREFIX; public static final String NAMESPACE_URL = "with-url"; public static final String NAMESPACE_URL_AND_METHOD = "with-url-and-method"; public static final String NAMESPACE_METRIC_ID = "with-metric-id"; public static final String NAMESPACE_FULL_RESPONSE = "full-response"; private static final Logger LOGGER = LoggerFactory.getLogger(Metrics.class); public static String buildMetricNamespace(String metricPrefix, String serverId) { if (metricPrefix != null) { if (serverId != null) { Metrics.LOGGER.warn("Metric prefix and server id both set. Using metric prefix '" + metricPrefix + "' for metric namespace."); } return metricPrefix + "." + HTTP_CLIENT_NAMESPACE_PREFIX; } else if (serverId != null) { return PUPPETLABS_NAMESPACE_PREFIX + "." + serverId + "." + HTTP_CLIENT_NAMESPACE_PREFIX; } else { return DEFAULT_NAMESPACE_PREFIX; } } public enum MetricType { FULL_RESPONSE } public enum MetricCategory { URL, URL_AND_METHOD, METRIC_ID } public static String urlToMetricUrl(String uriString) throws URISyntaxException { final URI uri = new URI(uriString); final URI convertedUri = new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null); return convertedUri.toString(); } private static List getUrlClientTimerArray(MetricRegistry registry, MetricFilter filter) { List timerArray = new ArrayList<>(); for (Map.Entry entry : registry.getTimers(filter).entrySet()) { UrlClientTimer timer = (UrlClientTimer)entry.getValue(); timerArray.add(timer); } return timerArray; } private static List getUrlAndMethodClientTimerArray(MetricRegistry registry, MetricFilter filter) { List timerArray = new ArrayList<>(); for (Map.Entry entry : registry.getTimers(filter).entrySet()) { UrlAndMethodClientTimer timer = (UrlAndMethodClientTimer)entry.getValue(); timerArray.add(timer); } return timerArray; } private static List getMetricIdClientTimerArray(MetricRegistry registry, MetricFilter filter) { List timerArray = new ArrayList<>(); for (Map.Entry entry : registry.getTimers(filter).entrySet()) { MetricIdClientTimer timer = (MetricIdClientTimer)entry.getValue(); timerArray.add(timer); } return timerArray; } public static ClientTimerContainer getClientMetrics(MetricRegistry metricRegistry){ if (metricRegistry != null) { return new ClientTimerContainer( getUrlClientTimerArray(metricRegistry, new CategoryClientTimerMetricFilter(MetricCategory.URL)), getUrlAndMethodClientTimerArray(metricRegistry, new CategoryClientTimerMetricFilter(MetricCategory.URL_AND_METHOD)), getMetricIdClientTimerArray(metricRegistry, new CategoryClientTimerMetricFilter(MetricCategory.METRIC_ID))); } else { throw new IllegalArgumentException("Metric registry must not be null"); } } public static List getClientMetricsByUrl(MetricRegistry metricRegistry, final String url){ if (metricRegistry != null) { return getUrlClientTimerArray(metricRegistry, new UrlClientTimerFilter(url)); } else { throw new IllegalArgumentException("Metric registry must not be null"); } } public static List getClientMetricsByUrlAndMethod(MetricRegistry metricRegistry, final String url, final String method){ if (metricRegistry != null) { return getUrlAndMethodClientTimerArray(metricRegistry, new UrlAndMethodClientTimerFilter(url, method)); } else { throw new IllegalArgumentException("Metric registry must not be null"); } } public static List getClientMetricsByMetricId(MetricRegistry metricRegistry, final String[] metricId){ if (metricRegistry != null) { if (metricId.length == 0) { return getMetricIdClientTimerArray(metricRegistry, new CategoryClientTimerMetricFilter(MetricCategory.METRIC_ID)); } else { return getMetricIdClientTimerArray(metricRegistry, new MetricIdClientTimerFilter(new ArrayList(Arrays.asList(metricId)))); } } else { throw new IllegalArgumentException("Metric registry must not be null"); } } private static List computeUrlClientMetricsData(List timers) { if (timers != null) { List metricsData = new ArrayList<>(); for (UrlClientTimer timer: timers) { TimerMetricData timerMetricData = TimerMetricData.fromTimer(timer); String url = timer.getUrl(); metricsData.add(new UrlClientMetricData(timerMetricData, url)); } return metricsData; } else { return null; } } private static List computeUrlAndMethodClientMetricsData(List timers) { if (timers != null) { List metricsData = new ArrayList<>(); for (UrlAndMethodClientTimer timer: timers) { TimerMetricData timerMetricData = TimerMetricData.fromTimer(timer); String url = timer.getUrl(); String method = timer.getMethod(); metricsData.add(new UrlAndMethodClientMetricData(timerMetricData, url, method)); } return metricsData; } else { return null; } } private static List computeMetricIdClientMetricsData(List timers) { if (timers != null) { List metricsData = new ArrayList<>(); for (MetricIdClientTimer timer: timers) { TimerMetricData timerMetricData = TimerMetricData.fromTimer(timer); List metricId = timer.getMetricId(); metricsData.add(new MetricIdClientMetricData(timerMetricData, metricId)); } return metricsData; } else { return null; } } public static ClientMetricDataContainer getClientMetricsData(MetricRegistry metricRegistry){ if ( metricRegistry != null ) { ClientTimerContainer timers = getClientMetrics(metricRegistry); return new ClientMetricDataContainer(computeUrlClientMetricsData(timers.getUrlTimers()), computeUrlAndMethodClientMetricsData(timers.getUrlAndMethodTimers()), computeMetricIdClientMetricsData(timers.getMetricIdTimers()) ); } else { throw new IllegalArgumentException("Metric registry must not be null"); } } public static List getClientMetricsDataByUrl(MetricRegistry metricRegistry, String url){ List timers = getClientMetricsByUrl(metricRegistry, url); return computeUrlClientMetricsData(timers); } public static List getClientMetricsDataByUrlAndMethod(MetricRegistry metricRegistry, String url, String method){ List timers = getClientMetricsByUrlAndMethod(metricRegistry, url, method); return computeUrlAndMethodClientMetricsData(timers); } public static List getClientMetricsDataByMetricId(MetricRegistry metricRegistry, String[] metricId){ List timers = getClientMetricsByMetricId(metricRegistry, metricId); return computeMetricIdClientMetricsData(timers); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/UrlAndMethodClientMetricData.java000066400000000000000000000007571373223535200333670ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import com.puppetlabs.http.client.impl.metrics.TimerMetricData; public class UrlAndMethodClientMetricData extends UrlClientMetricData { private final String method; public UrlAndMethodClientMetricData(TimerMetricData timerMetricData, String url, String method) { super(timerMetricData, url); this.method = method; } public String getMethod() { return method; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/UrlAndMethodClientTimer.java000066400000000000000000000011171373223535200324210ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; public class UrlAndMethodClientTimer extends UrlClientTimer { private final String method; public UrlAndMethodClientTimer(String metricName, String url, String method, Metrics.MetricType metricType) { super(metricName, url, metricType); this.method = method; } public String getMethod() { return method; } @Override public boolean isCategory(Metrics.MetricCategory category) { return category.equals(Metrics.MetricCategory.URL_AND_METHOD); } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/UrlClientMetricData.java000066400000000000000000000006561373223535200316010ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; import com.puppetlabs.http.client.impl.metrics.TimerMetricData; public class UrlClientMetricData extends ClientMetricData { private final String url; public UrlClientMetricData(TimerMetricData timerMetricData, String url) { super(timerMetricData); this.url = url; } public String getUrl() { return url; } } clj-http-client-1.2.0/src/java/com/puppetlabs/http/client/metrics/UrlClientTimer.java000066400000000000000000000007511373223535200306400ustar00rootroot00000000000000package com.puppetlabs.http.client.metrics; public class UrlClientTimer extends ClientTimer { private final String url; public UrlClientTimer(String metricName, String url, Metrics.MetricType metricType) { super(metricName, metricType); this.url = url; } public String getUrl() { return url; } @Override public boolean isCategory(Metrics.MetricCategory category) { return category.equals(Metrics.MetricCategory.URL); } } clj-http-client-1.2.0/test/000077500000000000000000000000001373223535200154775ustar00rootroot00000000000000clj-http-client-1.2.0/test/com/000077500000000000000000000000001373223535200162555ustar00rootroot00000000000000clj-http-client-1.2.0/test/com/puppetlabs/000077500000000000000000000000001373223535200204345ustar00rootroot00000000000000clj-http-client-1.2.0/test/com/puppetlabs/http/000077500000000000000000000000001373223535200214135ustar00rootroot00000000000000clj-http-client-1.2.0/test/com/puppetlabs/http/client/000077500000000000000000000000001373223535200226715ustar00rootroot00000000000000clj-http-client-1.2.0/test/com/puppetlabs/http/client/impl/000077500000000000000000000000001373223535200236325ustar00rootroot00000000000000clj-http-client-1.2.0/test/com/puppetlabs/http/client/impl/java_client_test.clj000066400000000000000000000047311373223535200276470ustar00rootroot00000000000000(ns com.puppetlabs.http.client.impl.java-client-test (:import (com.puppetlabs.http.client.impl JavaClient) (org.apache.commons.io IOUtils) (com.puppetlabs.http.client ResponseBodyType RequestOptions) (org.apache.http.entity ContentType) (java.io ByteArrayInputStream)) (:require [clojure.test :refer :all])) ;; NOTE: there are more comprehensive, end-to-end tests for ;; the Java client functionality lumped in with the clojure ;; tests. This namespace is just for some Java-only unit tests. (deftest test-coerce-body-type (testing "Can handle a Content Type header with no charset" (let [body "foo" body-stream (IOUtils/toInputStream body "UTF-8")] (is (= "foo" (JavaClient/coerceBodyType body-stream ResponseBodyType/TEXT ContentType/WILDCARD)))))) (defn request-options [body content-type-value] (new RequestOptions nil {"content-type" content-type-value} body false nil)) (defn compute-content-type [body content-type-value] (-> (JavaClient/getContentType body (request-options body content-type-value)) ;; Calling .toString on an instance of org.apache.http.entity.ContentType ;; generates the string that'll actually end up in the header. .toString)) ;; This test case is 100% copypasta from puppetlabs.http.client.async-test (deftest content-type-test (testing "value of content-type header is computed correctly" (testing "a byte stream which specifies application/octet-stream" (let [body (ByteArrayInputStream. (byte-array [(byte 1) (byte 2)]))] (is (= (compute-content-type body "application/octet-stream") "application/octet-stream")))) (testing "the request body is a string" (testing "when a charset is specified, it is honored" (let [body "foo"] (is (= (compute-content-type body "text/plain; charset=US-ASCII") "text/plain; charset=US-ASCII")))) (testing "a missing charset yields a content-type that maintains the given mime-type but adds UTF-8 as the charset" (let [body "foo"] (is (= (compute-content-type body "text/html") "text/html; charset=UTF-8"))))))) (deftest null-response-body-coerced-as-text (testing "a null response body is coerced into a string by JavaClient.coerceBodyType" (let [body nil] (is (= "" (JavaClient/coerceBodyType body ResponseBodyType/TEXT nil)))))) clj-http-client-1.2.0/test/com/puppetlabs/http/client/impl/metrics_unit_test.clj000066400000000000000000000407231373223535200300760ustar00rootroot00000000000000(ns com.puppetlabs.http.client.impl.metrics-unit-test (:require [clojure.test :refer :all] [puppetlabs.http.client.metrics :as metrics] [schema.test :as schema-test]) (:import (com.codahale.metrics MetricRegistry) (com.puppetlabs.http.client.metrics Metrics) (org.apache.http.message BasicHttpRequest) (clojure.lang ExceptionInfo) (com.puppetlabs.http.client.impl.metrics TimerUtils) (java.net URISyntaxException))) (use-fixtures :once schema-test/validate-schemas) (defn add-metric-ns [string] (str "puppetlabs.http-client.experimental." string)) (deftest start-full-response-timers-test (testing "startFullResponseTimers creates the right timers" (let [url-id (add-metric-ns "with-url.http://localhost/foo.full-response") url-method-id (add-metric-ns "with-url-and-method.http://localhost/foo.GET.full-response")] (testing "metric id timers are not created for a request without a metric id" (let [metric-registry (MetricRegistry.)] (TimerUtils/startFullResponseTimers metric-registry (BasicHttpRequest. "GET" "http://localhost/foo") nil Metrics/DEFAULT_NAMESPACE_PREFIX true) (is (= (set (list url-id url-method-id)) (set (keys (.getTimers metric-registry))))))) (testing "metric id timers are not created for a request with an empty metric id" (let [metric-registry (MetricRegistry.)] (TimerUtils/startFullResponseTimers metric-registry (BasicHttpRequest. "GET" "http://localhost/foo") (into-array String []) Metrics/DEFAULT_NAMESPACE_PREFIX true) (is (= (set (list url-id url-method-id)) (set (keys (.getTimers metric-registry))))))) (testing "metric id timers are created correctly for a request with a metric id" (let [metric-registry (MetricRegistry.)] (TimerUtils/startFullResponseTimers metric-registry (BasicHttpRequest. "GET" "http://localhost/foo") (into-array ["foo" "bar" "baz"]) Metrics/DEFAULT_NAMESPACE_PREFIX true) (is (= (set (list url-id url-method-id (add-metric-ns "with-metric-id.foo.full-response") (add-metric-ns "with-metric-id.foo.bar.full-response") (add-metric-ns "with-metric-id.foo.bar.baz.full-response"))) (set (keys (.getTimers metric-registry))))))) (testing "url timers should strip off username, password, query string, and fragment" (let [metric-registry (MetricRegistry.)] (TimerUtils/startFullResponseTimers metric-registry (BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?te%2cst=one") nil Metrics/DEFAULT_NAMESPACE_PREFIX true) (TimerUtils/startFullResponseTimers metric-registry (BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz#x%2cyz") nil Metrics/DEFAULT_NAMESPACE_PREFIX true) (TimerUtils/startFullResponseTimers metric-registry (BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?te%2cst=one#x%2cyz") nil Metrics/DEFAULT_NAMESPACE_PREFIX true) (TimerUtils/startFullResponseTimers metric-registry (BasicHttpRequest. "GET" "http://user:pwd@localhost:1234/foo%2cbar/baz?#x%2cyz") nil Metrics/DEFAULT_NAMESPACE_PREFIX true) (is (= (set (list (add-metric-ns "with-url.http://localhost:1234/foo,bar/baz.full-response") (add-metric-ns "with-url-and-method.http://localhost:1234/foo,bar/baz.GET.full-response"))) (set (keys (.getTimers metric-registry)))))))))) (deftest url->metric-url-test (testing "url->metric-url strips username, password, query params, and path fragment off of url" (let [url "http://user:pwd@localhost:1234/foo%2cbar/baz?te%2cst=one"] (is (= "http://localhost:1234/foo,bar/baz" (Metrics/urlToMetricUrl url) (metrics/url->metric-url url))))) (testing "url->metric-url throws error if non-url passed in" (is (thrown? URISyntaxException (Metrics/urlToMetricUrl "abc def"))) (is (thrown? URISyntaxException (metrics/url->metric-url "abc def"))))) (defn start-and-stop-timers! [registry req id] (doseq [timer (TimerUtils/startFullResponseTimers registry req id Metrics/DEFAULT_NAMESPACE_PREFIX true)] (.stop timer))) (deftest get-client-metrics-data-test (let [registry (MetricRegistry.) url "http://test.com/one" url2 "http://test.com/one/two"] (start-and-stop-timers! registry (BasicHttpRequest. "GET" url) nil) (start-and-stop-timers! registry (BasicHttpRequest. "POST" url) nil) (start-and-stop-timers! registry (BasicHttpRequest. "POST" url) (into-array ["foo" "bar"])) (start-and-stop-timers! registry (BasicHttpRequest. "GET" url2) (into-array ["foo" "abc"])) (testing "getClientMetrics without args returns all timers organized by category" (is (= (set [:url :url-and-method :metric-id]) (set (keys (metrics/get-client-metrics registry))) (set (keys (metrics/get-client-metrics-data registry))))) (is (= (set [(add-metric-ns "with-url.http://test.com/one.full-response") (add-metric-ns "with-url.http://test.com/one/two.full-response")]) (set (map #(.getMetricName %) (.getUrlTimers (Metrics/getClientMetrics registry)))) (set (map #(.getMetricName %) (:url (metrics/get-client-metrics registry)))) (set (map #(.getMetricName %) (.getUrlData (Metrics/getClientMetricsData registry)))) (set (map :metric-name (:url (metrics/get-client-metrics-data registry)))))) (is (= (set [(add-metric-ns "with-url-and-method.http://test.com/one.GET.full-response") (add-metric-ns "with-url-and-method.http://test.com/one.POST.full-response") (add-metric-ns "with-url-and-method.http://test.com/one/two.GET.full-response")]) (set (map #(.getMetricName %) (.getUrlAndMethodTimers (Metrics/getClientMetrics registry)))) (set (map #(.getMetricName %) (:url-and-method (metrics/get-client-metrics registry)))) (set (map #(.getMetricName %) (.getUrlAndMethodData (Metrics/getClientMetricsData registry)))) (set (map :metric-name (:url-and-method (metrics/get-client-metrics-data registry)))))) (is (= (set ["puppetlabs.http-client.experimental.with-metric-id.foo.full-response" "puppetlabs.http-client.experimental.with-metric-id.foo.bar.full-response" "puppetlabs.http-client.experimental.with-metric-id.foo.abc.full-response"]) (set (map #(.getMetricName %) (.getMetricIdTimers (Metrics/getClientMetrics registry)))) (set (map #(.getMetricName %) (:metric-id (metrics/get-client-metrics registry)))) (set (map #(.getMetricName %) (.getMetricIdData (Metrics/getClientMetricsData registry)))) (set (map :metric-name (:metric-id (metrics/get-client-metrics-data registry))))))) (testing "getClientMetricsData with url returns the right thing" (let [java-data (Metrics/getClientMetricsDataByUrl registry url) clj-data (metrics/get-client-metrics-data-by-url registry url)] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-url.http://test.com/one.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 3 (.getCount (first java-data)) (:count (first clj-data))))) (let [java-data (Metrics/getClientMetricsDataByUrl registry url2) clj-data (metrics/get-client-metrics-data-by-url registry url2)] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-url.http://test.com/one/two.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 1 (.getCount (first java-data)) (:count (first clj-data))))) (testing "getClientMetricsData with url returns nothing if url is not a full match" (is (= [] (Metrics/getClientMetricsDataByUrl registry "http://test.com") (metrics/get-client-metrics-data-by-url registry "http://test.com"))))) (testing "getClientMetricsData with url and method returns the right thing" (let [java-data (Metrics/getClientMetricsDataByUrlAndMethod registry url "GET") clj-data (metrics/get-client-metrics-data-by-url-and-method registry url :get)] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-url-and-method.http://test.com/one.GET.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 1 (.getCount (first java-data)) (:count (first clj-data))))) (let [java-data (Metrics/getClientMetricsDataByUrlAndMethod registry url "POST") clj-data (metrics/get-client-metrics-data-by-url-and-method registry url :post)] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-url-and-method.http://test.com/one.POST.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 2 (.getCount (first java-data)) (:count (first clj-data))))) (let [java-data (Metrics/getClientMetricsDataByUrlAndMethod registry url2 "GET") clj-data (metrics/get-client-metrics-data-by-url-and-method registry url2 :get)] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-url-and-method.http://test.com/one/two.GET.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 1 (.getCount (first java-data)) (:count (first clj-data))))) (testing "getClientMetricsData with url and method returns nothing if method is not a match" (is (= [] (Metrics/getClientMetricsDataByUrlAndMethod registry "http://test.com" "PUT") (metrics/get-client-metrics-data-by-url-and-method registry "http://test.com" :put))))) (testing "getClientMetricsData with metric id returns the right thing" (let [java-data (Metrics/getClientMetricsDataByMetricId registry (into-array ["foo"])) clj-data (metrics/get-client-metrics-data-by-metric-id registry ["foo"])] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-metric-id.foo.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 2 (.getCount (first java-data)) (:count (first clj-data))))) (let [java-data (Metrics/getClientMetricsDataByMetricId registry (into-array ["foo" "bar"])) clj-data (metrics/get-client-metrics-data-by-metric-id registry ["foo" "bar"])] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-metric-id.foo.bar.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 1 (.getCount (first java-data)) (:count (first clj-data))))) (let [java-data (Metrics/getClientMetricsDataByMetricId registry (into-array ["foo" "abc"])) clj-data (metrics/get-client-metrics-data-by-metric-id registry ["foo" "abc"])] (is (= 1 (count java-data) (count clj-data))) (is (= (add-metric-ns "with-metric-id.foo.abc.full-response") (.getMetricName (first java-data)) (:metric-name (first clj-data)))) (is (= 1 (.getCount (first java-data)) (:count (first clj-data)))) (testing "metric id can be specified as keyword or string" (is (= clj-data (metrics/get-client-metrics-data-by-metric-id registry ["foo" :abc]))))) (testing "getClientMetricsData with metric id returns nothing if id is not a match" (is (= [] (Metrics/getClientMetricsDataByMetricId registry (into-array ["foo" "cat"])) (metrics/get-client-metrics-data-by-metric-id registry ["foo" "cat"])))) (testing "getClientMetrics|Data returns throws an error if no metric registry passed in" (is (thrown? ExceptionInfo (metrics/get-client-metrics nil))) (is (thrown? ExceptionInfo (metrics/get-client-metrics-data nil))) (is (thrown? IllegalArgumentException (Metrics/getClientMetrics nil))) (is (thrown? IllegalArgumentException (Metrics/getClientMetricsData nil)))) (testing (str "getClientMetrics|Data returns data structure with empty arrays" " as values if no requests have been made yet") (let [empty-metrics (Metrics/getClientMetrics (MetricRegistry.)) empty-metrics-data (Metrics/getClientMetricsData (MetricRegistry.))] (is (= {:url [] :url-and-method [] :metric-id []} (metrics/get-client-metrics (MetricRegistry.)) (metrics/get-client-metrics-data (MetricRegistry.)))) (is (empty? (.getUrlTimers empty-metrics))) (is (empty? (.getUrlAndMethodTimers empty-metrics))) (is (empty? (.getMetricIdTimers empty-metrics))) (is (empty? (.getUrlData empty-metrics-data))) (is (empty? (.getUrlAndMethodData empty-metrics-data))) (is (empty? (.getMetricIdData empty-metrics-data))))) (testing "getClientMetrics returns correctly without metric-id on request" (let [registry (MetricRegistry.) url "http://test.com/one"] (start-and-stop-timers! registry (BasicHttpRequest. "GET" url) nil) (let [client-metrics (Metrics/getClientMetrics registry) client-metrics-data (Metrics/getClientMetricsData registry)] (is (= (set [(add-metric-ns "with-url.http://test.com/one.full-response")]) (set (map #(.getMetricName %) (.getUrlTimers client-metrics))) (set (map #(.getMetricName %) (.getUrlData client-metrics-data))))) (is (= (set [(add-metric-ns "with-url-and-method.http://test.com/one.GET.full-response")]) (set (map #(.getMetricName %) (.getUrlAndMethodTimers client-metrics))) (set (map #(.getMetricName %) (.getUrlAndMethodData client-metrics-data))))) (is (= [] (.getMetricIdTimers client-metrics) (.getMetricIdData client-metrics-data))))))))) (deftest empty-metric-id-filter-test (testing "a metric id filter with an empty array returns all metric id timers" (let [registry (MetricRegistry.) url "http://test.com/foo/bar" foo-id (add-metric-ns "with-metric-id.foo.full-response") foo-bar-id (add-metric-ns "with-metric-id.foo.bar.full-response") foo-bar-baz-id (add-metric-ns "with-metric-id.foo.bar.baz.full-response")] (start-and-stop-timers! registry (BasicHttpRequest. "GET" url) (into-array ["foo" "bar" "baz"])) (testing "empty metric filter returns all metric id timers" (is (= (set (list foo-id foo-bar-id foo-bar-baz-id)) (set (map #(.getMetricName %) (Metrics/getClientMetricsDataByMetricId registry (into-array String [])))) (set (map :metric-name (metrics/get-client-metrics-data-by-metric-id registry []))))))))) clj-http-client-1.2.0/test/puppetlabs/000077500000000000000000000000001373223535200176565ustar00rootroot00000000000000clj-http-client-1.2.0/test/puppetlabs/http/000077500000000000000000000000001373223535200206355ustar00rootroot00000000000000clj-http-client-1.2.0/test/puppetlabs/http/client/000077500000000000000000000000001373223535200221135ustar00rootroot00000000000000clj-http-client-1.2.0/test/puppetlabs/http/client/async_plaintext_test.clj000066400000000000000000000725621373223535200270650ustar00rootroot00000000000000(ns puppetlabs.http.client.async-plaintext-test (:import (com.puppetlabs.http.client Async RequestOptions ClientOptions) (org.apache.http.impl.nio.client HttpAsyncClients) (java.net URI SocketTimeoutException ServerSocket URL) (java.util Locale) (java.util.concurrent CountDownLatch TimeUnit)) (:require [clojure.test :refer :all] [puppetlabs.http.client.test-common :refer :all] [puppetlabs.i18n.core :as i18n] [puppetlabs.trapperkeeper.core :as tk] [puppetlabs.trapperkeeper.testutils.bootstrap :as testutils] [puppetlabs.trapperkeeper.testutils.logging :as testlogging] [puppetlabs.trapperkeeper.testutils.webserver :as testwebserver] [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9] [puppetlabs.http.client.common :as common] [puppetlabs.http.client.async :as async] [schema.test :as schema-test] [ring.middleware.cookies :refer [wrap-cookies]])) (use-fixtures :once schema-test/validate-schemas) (defn app [_] {:status 200 :body "Hello, World!"}) (defn app-with-empty-content-type [_] {:headers {"content-type" ""} :status 200 :body "Hello, World!"}) (defn app-with-language-header-echo [{{:strs [accept-language]} :headers}] {:status 200 :body (str accept-language)}) (tk/defservice test-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler app "/hello") context)) (defn cookie-handler [_] {:status 200 :body "cookie has been set" :cookies {"session_id" {:value "session-id-hash"}}}) (defn check-cookie-handler [req] (if (empty? (get req :cookies)) {:status 400 :body "cookie has not been set"} {:status 200 :body "cookie has been set"})) (tk/defservice test-cookie-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler (wrap-cookies cookie-handler) "/cookietest") (add-ring-handler (wrap-cookies check-cookie-handler) "/cookiecheck") context)) (deftest persistent-async-client-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:port 10000}} (testing "java async client" (let [request-options (RequestOptions. (URI. "http://localhost:10000/hello/")) client-options (ClientOptions.) client (Async/createClient client-options)] (testing "HEAD request to missing endpoint" (let [response (.head client (RequestOptions. (URI. "http://localhost:10000/missing")))] (is (= 404 (.getStatus (.deref response)))) (is (= "Not Found" (.getReasonPhrase (.deref response)))))) (testing "HEAD request with persistent async client" (let [response (.head client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "OK" (.getReasonPhrase (.deref response)))) (is (= nil (.getBody (.deref response)))))) (testing "GET request with persistent async client" (let [response (.get client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response))))))) (testing "POST request with persistent async client" (let [response (.post client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response))))))) (testing "PUT request with persistent async client" (let [response (.put client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response))))))) (testing "DELETE request with persistent async client" (let [response (.delete client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response))))))) (testing "TRACE request with persistent async client" (let [response (.trace client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response))))))) (testing "OPTIONS request with persistent async client" (let [response (.options client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response))))))) (testing "PATCH request with persistent async client" (let [response (.patch client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response))))))) (testing "client closes properly" (.close client) (is (thrown? IllegalStateException (.get client request-options)))))) (testing "clojure async client" (let [client (async/create-client {})] (testing "HEAD request to missing endpoint" (let [response (common/head client "http://localhost:10000/missing")] (is (= 404 (:status @response))) (is (= "Not Found" (:reason-phrase @response))))) (testing "HEAD request with persistent async client" (let [response (common/head client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "OK" (:reason-phrase @response))) (is (= nil (:body @response))))) (testing "GET request with persistent async client" (let [response (common/get client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "POST request with persistent async client" (let [response (common/post client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "PUT request with persistent async client" (let [response (common/put client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "DELETE request with persistent async client" (let [response (common/delete client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "TRACE request with persistent async client" (let [response (common/trace client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "OPTIONS request with persistent async client" (let [response (common/options client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "PATCH request with persistent async client" (let [response (common/patch client "http://localhost:10000/hello/")] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "GET request via request function with persistent async client" (let [response (common/make-request client "http://localhost:10000/hello/" :get)] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "Bad verb request via request function with persistent async client" (is (thrown? IllegalArgumentException (common/make-request client "http://localhost:10000/hello/" :bad)))) (testing "client closes properly" (common/close client) (is (thrown? IllegalStateException (common/get client "http://localhost:10000/hello/"))))))))) (deftest java-api-cookie-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-cookie-service] {:webserver {:port 10000}} (let [client (Async/createClient (ClientOptions.))] (testing "Set a cookie using Java API" (let [response (.get client (RequestOptions. (URI. "http://localhost:10000/cookietest")))] (is (= 200 (.getStatus (.deref response)))))) (testing "Check if cookie still exists" (let [response (.get client (RequestOptions. (URI. "http://localhost:10000/cookiecheck")))] (is (= 200 (.getStatus (.deref response)))))))))) (deftest clj-api-cookie-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-cookie-service] {:webserver {:port 10000}} (let [client (async/create-client {})] (testing "Set a cookie using Clojure API" (let [response (common/get client "http://localhost:10000/cookietest")] (is (= 200 (:status @response))))) (testing "Check if cookie still exists" (let [response (common/get client "http://localhost:10000/cookiecheck")] (is (= 200 (:status @response))))))))) (deftest request-with-client-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:port 10000}} (let [client (HttpAsyncClients/createDefault) opts {:method :get :url "http://localhost:10000/hello/"}] (.start client) (testing "GET request works with request-with-client" (let [response (async/request-with-client opts nil client)] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (testing "Client persists when passed to request-with-client" (let [response (async/request-with-client opts nil client)] (is (= 200 (:status @response))) (is (= "Hello, World!" (slurp (:body @response)))))) (.close client))))) (deftest query-params-test-async (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-params-web-service] {:webserver {:port 8080}} (testing "URI Query Parameters work with the Java client" (let [client (Async/createClient (ClientOptions.))] (try (let [request-options (RequestOptions. (URI. "http://localhost:8080/params?foo=bar&baz=lux")) response (.get client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= queryparams (read-string (slurp (.getBody (.deref response))))))) (finally (.close client))))) (testing "string URL Query Parameters work with the clojure client" (with-open [client (async/create-client {})] (let [opts {:method :get :url "http://localhost:8080/params/" :query-params queryparams :as :text} response (common/get client "http://localhost:8080/params" opts)] (is (= 200 (:status @response))) (is (= queryparams (read-string (:body @response))))))) (testing "URL based Query Parameters work with the clojure client" (with-open [client (async/create-client {})] (let [opts {:method :get :url (URL. "http://localhost:8080/params/") :query-params queryparams :as :text} response (common/get client "http://localhost:8080/params" opts)] (is (= 200 (:status @response))) (is (= queryparams (read-string (:body @response))))))) (testing "URI based Query Parameters work with the clojure client" (with-open [client (async/create-client {})] (let [opts {:method :get :url (.toURI (URL. "http://localhost:8080/params/")) :query-params queryparams :as :text} response (common/get client "http://localhost:8080/params" opts)] (is (= 200 (:status @response))) (is (= queryparams (read-string (:body @response))))))) (testing "string URL Query Parameters can be set directly in the URL" (with-open [client (async/create-client {})] (let [response (common/get client "http://localhost:8080/params?paramone=one" {:as :text})] (is (= 200 (:status @response))) (is (= (str {"paramone" "one"}) (:body @response)))))) (testing (str "string URL Query Parameters set in URL are overwritten if params " "are also specified in options map") (with-open [client (async/create-client {})] (let [response (common/get client "http://localhost:8080/params?paramone=one&foo=lux" query-options)] (is (= 200 (:status @response))) (is (= queryparams (read-string (:body @response)))))))))) (deftest redirect-test-async (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service redirect-web-service] {:webserver {:port 8080}} (testing (str "redirects on POST not followed by persistent Java client " "when forceRedirects option not set to true") (let [client (Async/createClient (ClientOptions.))] (try (let [request-options (RequestOptions. (URI. "http://localhost:8080/hello")) response (.post client request-options)] (is (= 302 (.getStatus (.deref response))))) (finally (.close client))))) (testing "redirects on POST followed by Java client when option is set" (let [client (Async/createClient (.. (ClientOptions.) (setForceRedirects true)))] (try (let [request-options (RequestOptions. (URI. "http://localhost:8080/hello")) response (.post client request-options)] (is (= 200 (.getStatus (.deref response)))) (is (= "Hello, World!" (slurp (.getBody (.deref response)))))) (finally (.close client))))) (testing "redirects not followed by Java client when :follow-redirects is false" (let [client (Async/createClient (.. (ClientOptions.) (setFollowRedirects false)))] (try (let [request-options (RequestOptions. (URI. "http://localhost:8080/hello")) response (.get client request-options)] (is (= 302 (.getStatus (.deref response))))) (finally (.close client))))) (testing ":follow-redirects overrides :force-redirects for Java client" (let [client (Async/createClient (.. (ClientOptions.) (setFollowRedirects false) (setForceRedirects true)))] (try (let [request-options (RequestOptions. (URI. "http://localhost:8080/hello")) response (.get client request-options)] (is (= 302 (.getStatus (.deref response))))) (finally (.close client))))) (testing (str "redirects on POST not followed by clojure client " "when :force-redirects is not set to true") (with-open [client (async/create-client {:force-redirects false})] (let [opts {:method :post :url "http://localhost:8080/hello" :as :text} response (common/post client "http://localhost:8080/hello" opts)] (is (= 302 (:status @response)))))) (testing (str "redirects on POST followed by persistent clojure client " "when option is set") (with-open [client (async/create-client {:force-redirects true})] (let [response (common/post client "http://localhost:8080/hello" {:as :text})] (is (= 200 (:status @response))) (is (= "Hello, World!" (:body @response)))))) (testing (str "persistent clojure client does not follow redirects when " ":follow-redirects is set to false") (with-open [client (async/create-client {:follow-redirects false})] (let [response (common/get client "http://localhost:8080/hello" {:as :text})] (is (= 302 (:status @response)))))) (testing ":follow-redirects overrides :force-redirects with persistent clj client" (with-open [client (async/create-client {:follow-redirects false :force-redirects true})] (let [response (common/get client "http://localhost:8080/hello" {:as :text})] (is (= 302 (:status @response))))))))) (deftest short-connect-timeout-persistent-java-test-async (testing (str "connection times out properly for java persistent client " "async request with short timeout") (with-open [client (-> (ClientOptions.) (.setConnectTimeoutMilliseconds 250) (Async/createClient))] (let [request-options (RequestOptions. "http://127.0.0.255:65535") time-before-connect (System/currentTimeMillis)] (is (connect-exception-thrown? (-> client (.get request-options) (.deref) (.getError))) "Unexpected result for connection attempt") (is (elapsed-within-range? time-before-connect 2000) "Connection attempt took significantly longer than timeout"))))) (deftest short-connect-timeout-persistent-clojure-test-async (testing (str "connection times out properly for clojure persistent client " "async request with short timeout") (with-open [client (async/create-client {:connect-timeout-milliseconds 250})] (let [time-before-connect (System/currentTimeMillis)] (is (connect-exception-thrown? (-> @(common/get client "http://127.0.0.255:65535") :error)) "Unexpected result for connection attempt") (is (elapsed-within-range? time-before-connect 2000) "Connection attempt took significantly longer than timeout"))))) (deftest longer-connect-timeout-test-async (testing "connection succeeds for async request with longer connect timeout" (testlogging/with-test-logging (testwebserver/with-test-webserver app port (let [url (str "http://localhost:" port "/hello")] (testing "java persistent async client" (with-open [client (-> (ClientOptions.) (.setConnectTimeoutMilliseconds 2000) (Async/createClient))] (let [response (-> client (.get (RequestOptions. url)) (.deref))] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response))))))) (testing "clojure persistent async client" (with-open [client (async/create-client {:connect-timeout-milliseconds 2000})] (let [response @(common/get client url {:as :text})] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))))))))))) (deftest short-socket-timeout-persistent-java-test-async (testing (str "socket read times out properly for persistent java async " "request with short timeout") (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 1) (Async/createClient)) server (ServerSocket. 0)] (let [request-options (-> "http://127.0.0.1:" (str (.getLocalPort server)) (RequestOptions.)) time-before-connect (System/currentTimeMillis)] (is (instance? SocketTimeoutException (-> client (.get request-options) (.deref) (.getError))) "Unexpected result for get attempt") (is (elapsed-within-range? time-before-connect 2000) "Get attempt took significantly longer than timeout"))))) (deftest short-socket-timeout-persistent-clojure-test-async (testing (str "socket read times out properly for clojure persistent client " "async request with short timeout") (with-open [client (async/create-client {:socket-timeout-milliseconds 250}) server (ServerSocket. 0)] (let [url (str "http://127.0.0.1:" (.getLocalPort server)) time-before-connect (System/currentTimeMillis)] (is (instance? SocketTimeoutException (-> @(common/get client url) :error)) "Unexpected result for get attempt") (is (elapsed-within-range? time-before-connect 2000) "Get attempt took significantly longer than timeout"))))) (deftest longer-socket-timeout-test-async (testing "get succeeds for async request with longer socket timeout" (testlogging/with-test-logging (testwebserver/with-test-webserver app port (let [url (str "http://localhost:" port "/hello")] (testing "java persistent async client" (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 2000) (Async/createClient))] (let [response (-> client (.get (RequestOptions. url)) (.deref))] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response))))))) (testing "clojure persistent async client" (with-open [client (async/create-client {:socket-timeout-milliseconds 2000})] (let [response @(common/get client url {:as :text})] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))))))))))) (deftest empty-content-type-async (testing "content-type parsing handles invalid content-type" (testlogging/with-test-logging ;; while the content-type is empty, the request actually gets sent ;; as ";", which is invalid (testwebserver/with-test-webserver app-with-empty-content-type port (let [url (str "http://localhost:" port "/hello")] (testing "java persistent async client" (with-open [client (-> (ClientOptions.) (Async/createClient))] (let [response (-> client (.get (RequestOptions. url)) (.deref))] (is (= 200 (.getStatus response)))))) (testing "clojure persistent async client" (with-open [client (async/create-client {})] (let [response @(common/get client url {:as :text})] (is (= 200 (:status response))))))))))) (deftest accept-language-async (testing "client passes on the user-locale in Accept-Language header" (testlogging/with-test-logging (testwebserver/with-test-webserver app-with-language-header-echo port (i18n/with-user-locale (Locale. "es" "ES") (let [url (str "http://localhost:" port "/hello")] (testing "clojure persistent async client" (with-open [client (async/create-client {})] (let [response @(common/get client url {:as :text})] (is (= 200 (:status response))) (is (= "es-ES" (:body response)))))))))))) (deftest client-route-and-total-limits (testing "client limits 2 requests per route by default" (let [actual-count (atom 0) countdown (CountDownLatch. 3) fake-app (fn [_] (swap! actual-count inc) (.countDown countdown) (.await countdown) {:status 200 :body "Hello, World!"})] (testlogging/with-test-logging (testwebserver/with-test-webserver fake-app port (let [url (str "http://localhost:" port "/hello")] (testing "clojure persistent async client" (with-open [client (async/create-client {})] (dotimes [n 10] (future (common/get client url {:as :text}))) (is (= false (.await countdown 5 TimeUnit/SECONDS))) (is (= 2 @actual-count))))))))) (testing "passing client route limit of 0 selects default behavior (a limit of 2)" (let [actual-count (atom 0) countdown (CountDownLatch. 3) fake-app (fn [_] (swap! actual-count inc) (.countDown countdown) (.await countdown) {:status 200 :body "Hello, World!"})] (testlogging/with-test-logging (testwebserver/with-test-webserver fake-app port (let [url (str "http://localhost:" port "/hello")] (testing "clojure persistent async client" (with-open [client (async/create-client {:max-connections-per-route 0})] (dotimes [n 10] (future (common/get client url {:as :text}))) (is (= false (.await countdown 5 TimeUnit/SECONDS))) (is (= 2 @actual-count))))))))) (testing "client limits specified requests per route" (let [actual-count (atom 0) countdown (CountDownLatch. 4) fake-app (fn [_] (swap! actual-count inc) (.countDown countdown) (.await countdown) {:status 200 :body "Hello, World!"})] (testlogging/with-test-logging (testwebserver/with-test-webserver fake-app port (let [url (str "http://localhost:" port "/hello")] (testing "clojure persistent async client" (with-open [client (async/create-client {:max-connections-per-route 3})] (dotimes [n 10] (future (common/get client url {:as :text}))) (is (= false (.await countdown 5 TimeUnit/SECONDS))) (is (= 3 @actual-count))))))))) (testing "client route limit of 11 does not limit requests per route when less than 11" (let [actual-count (atom 0) countdown (CountDownLatch. 10) fake-app (fn [_] (swap! actual-count inc) (.countDown countdown) (.await countdown) {:status 200 :body "Hello, World!"})] (testlogging/with-test-logging (testwebserver/with-test-webserver fake-app port (let [url (str "http://localhost:" port "/hello")] (testing "clojure persistent async client" (with-open [client (async/create-client {:max-connections-per-route 11})] (dotimes [n 10] (future (common/get client url {:as :text}))) (is (= true (.await countdown 5 TimeUnit/SECONDS))) (is (= 10 @actual-count))))))))) (testing "overall limit applies" (let [actual-count (atom 0) countdown (CountDownLatch. 4) fake-app (fn [_] (swap! actual-count inc) (.countDown countdown) (.await countdown) {:status 200 :body "Hello, World!"})] (testlogging/with-test-logging (testwebserver/with-test-webserver fake-app port (let [url (str "http://localhost:" port "/hello")] (testing "clojure persistent async client" (with-open [client (async/create-client {:max-connections-per-route 11 :max-connections-total 3})] (dotimes [n 10] (future (common/get client url {:as :text}))) (is (= false (.await countdown 5 TimeUnit/SECONDS))) (is (= 3 @actual-count)))))))))) clj-http-client-1.2.0/test/puppetlabs/http/client/async_unbuffered_test.clj000066400000000000000000000421021373223535200271650ustar00rootroot00000000000000(ns puppetlabs.http.client.async-unbuffered-test (:import (com.puppetlabs.http.client Async RequestOptions ClientOptions ResponseBodyType) (java.net SocketTimeoutException ConnectException) (java.io PipedInputStream PipedOutputStream) (java.util.concurrent TimeoutException) (java.util UUID)) (:require [clojure.test :refer :all] [puppetlabs.http.client.test-common :refer :all] [puppetlabs.trapperkeeper.testutils.logging :as testlogging] [puppetlabs.trapperkeeper.testutils.webserver :as testwebserver] [puppetlabs.http.client.common :as common] [puppetlabs.http.client.async :as async] [schema.test :as schema-test])) (use-fixtures :once schema-test/validate-schemas) (defn generate-data "Generate data of approximately the requested size, which is moderately compressible" [data-size] (apply str "xxxx" (repeatedly (/ data-size 35) #(UUID/randomUUID)))) (defn successful-handler "A Ring handler that asynchronously sends some data, waits for confirmation the data has been received then sends some more data" [data send-more-data] (fn [_] (let [outstream (PipedOutputStream.) instream (PipedInputStream.)] (.connect instream outstream) ;; Return the response immediately and asynchronously stream some data into it (future (.write outstream (.getBytes data)) ; Block until the client confirms it has read the first few bytes ; :socket-timeout-milliseconds on the client ensures we can't really get stuck here, even if the test fails (if send-more-data (deref send-more-data)) ; Write the last of the data (.write outstream (.getBytes "yyyy")) (.close outstream)) {:status 200 :body instream}))) (defn blocking-handler "A Ring handler that sends some data but then never closes the socket" [data] (fn [_] (let [outstream (PipedOutputStream.) instream (PipedInputStream.)] (.connect instream outstream) ;; Return the response immediately and asynchronously stream some data into it (future (.write outstream (.getBytes data))) {:status 200 :body instream}))) (defn- clojure-non-blocking-streaming "Stream 32M of data (roughly) which is large enough to ensure the client won't buffer it all. Checks the data is streamed in a non-blocking manner i.e some data is received by the client before the server has finished transmission" [decompress-body?] (testlogging/with-test-logging (let [data (generate-data (* 32 1024 1024)) opts {:as :unbuffered-stream :decompress-body decompress-body?}] (testing " - check data can be streamed successfully" (let [send-more-data (promise)] (testwebserver/with-test-webserver-and-config (successful-handler data send-more-data) port {:shutdown-timeout-seconds 1} (with-open [client (async/create-client {:connect-timeout-milliseconds 100 :socket-timeout-milliseconds 20000})] (let [response @(common/get client (str "http://localhost:" port "/hello") opts) {:keys [status body]} response] (is (= 200 status)) (let [instream body buf (make-array Byte/TYPE 4) _ (.read instream buf)] (is (= "xxxx" (String. buf "UTF-8"))) ;; Make sure we can read a few chars off of the stream (deliver send-more-data true) ;; Indicate we read some chars (is (= (str data "yyyy") (str "xxxx" (slurp instream)))))))))) ;; Read the rest and validate (testing " - check socket timeout is handled" (try (testwebserver/with-test-webserver-and-config (blocking-handler data) port {:shutdown-timeout-seconds 1} (with-open [client (async/create-client {:connect-timeout-milliseconds 100 :socket-timeout-milliseconds 200})] (let [response @(common/get client (str "http://localhost:" port "/hello") opts) {:keys [body error]} response] (is (nil? error)) ;; Consume the body to get the exception (is (thrown? SocketTimeoutException (slurp body)))))) (catch TimeoutException e ;; Expected whenever a server-side failure is generated nil))) (testing " - check connection timeout is handled" (with-open [client (async/create-client {:connect-timeout-milliseconds 100})] (let [response @(common/get client (str "http://localhost:" 12345 "/bad") opts) {:keys [error]} response] (is error) (is (instance? ConnectException error)))))))) (deftest clojure-non-blocking-streaming-without-decompression (testing "clojure :unbuffered-stream with 32MB payload and no decompression" (clojure-non-blocking-streaming false))) (deftest clojure-non-blocking-streaming-with-decompression (testing "clojure :unbuffered-stream with 32MB payload and decompression" (clojure-non-blocking-streaming true))) (defn- clojure-blocking-streaming "Stream data that is buffered client-side i.e. in a blocking manner" [data opts] (testlogging/with-test-logging (testing " - check data can be streamed successfully" (testwebserver/with-test-webserver-and-config (successful-handler data nil) port {:shutdown-timeout-seconds 1} (with-open [client (async/create-client {:connect-timeout-milliseconds 100 :socket-timeout-milliseconds 20000})] (let [response @(common/get client (str "http://localhost:" port "/hello") opts) {:keys [status body]} response] (is (= 200 status)) (let [instream body buf (make-array Byte/TYPE 4) _ (.read instream buf)] (is (= "xxxx" (String. buf "UTF-8"))) ;; Make sure we can read a few chars off of the stream (is (= (str data "yyyy") (str "xxxx" (slurp instream))))))))) ;; Read the rest and validate (testing " - check socket timeout is handled" (try (testwebserver/with-test-webserver-and-config (blocking-handler data) port {:shutdown-timeout-seconds 1} (with-open [client (async/create-client {:connect-timeout-milliseconds 100 :socket-timeout-milliseconds 200})] (let [response @(common/get client (str "http://localhost:" port "/hello") opts) {:keys [error]} response] (is (instance? SocketTimeoutException error))))) (catch TimeoutException e ;; Expected whenever a server-side failure is generated nil))) (testing " - check connection timeout is handled" (with-open [client (async/create-client {:connect-timeout-milliseconds 100})] (let [response @(common/get client (str "http://localhost:" 12345 "/bad") opts) {:keys [error]} response] (is error) (is (instance? ConnectException error))))))) (deftest clojure-blocking-streaming-without-decompression (testing "clojure :unbuffered-stream with 1K payload and no decompression" ;; This is a small enough payload that :unbuffered-stream still buffers it all in memory and so it behaves ;; identically to :stream (clojure-blocking-streaming (generate-data 1024) {:as :unbuffered-stream :decompress-body false}))) (deftest clojure-blocking-streaming-with-decompression (testing "clojure :unbuffered-stream with 1K payload and decompression" ;; This is a small enough payload that :unbuffered-stream still buffers it all in memory and so it behaves ;; identically to :stream (clojure-blocking-streaming (generate-data 1024) {:as :unbuffered-stream :decompress-body true}))) (deftest clojure-existing-streaming-with-small-payload-without-decompression (testing "clojure :stream with 1K payload and no decompression" (clojure-blocking-streaming (generate-data 1024) {:as :stream :decompress-body false}))) (deftest clojure-existing-streaming-with-small-payload-with-decompression (testing "clojure :stream with 1K payload and decompression" (clojure-blocking-streaming (generate-data 1024) {:as :stream :decompress-body true}))) (deftest clojure-existing-streaming-with-large-payload-without-decompression (testing "clojure :stream with 32M payload and no decompression" (clojure-blocking-streaming (generate-data (* 32 1024 1024)) {:as :stream :decompress-body false}))) (deftest clojure-existing-streaming-with-large-payload-with-decompression (testing "clojure :stream with 32M payload and decompression" (clojure-blocking-streaming (generate-data (* 32 1024 1024)) {:as :stream :decompress-body true}))) (defn- java-non-blocking-streaming "Stream 32M of data (roughly) which is large enough to ensure the client won't buffer it all. Checks the data is streamed in a non-blocking manner i.e some data is received by the client before the server has finished transmission" [decompress-body?] (testlogging/with-test-logging (let [data (generate-data (* 32 1024 1024))] (testing " - check data can be streamed successfully" (let [send-more-data (promise)] (testwebserver/with-test-webserver-and-config (successful-handler data send-more-data) port {:shutdown-timeout-seconds 1} (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 20000) (.setConnectTimeoutMilliseconds 100) (Async/createClient))] (let [request-options (doto (RequestOptions. (str "http://localhost:" port "/hello")) (.setAs ResponseBodyType/UNBUFFERED_STREAM) (.setDecompressBody decompress-body?)) response (-> client (.get request-options) .deref) status (.getStatus response) body (.getBody response)] (is (= 200 status)) (let [instream body buf (make-array Byte/TYPE 4) _ (.read instream buf)] (is (= "xxxx" (String. buf "UTF-8"))) ;; Make sure we can read a few chars off of the stream (deliver send-more-data true) ;; Indicate we read some chars (is (= (str data "yyyy") (str "xxxx" (slurp instream)))))))))) ;; Read the rest and validate (testing " - check socket timeout is handled" (try (testwebserver/with-test-webserver-and-config (blocking-handler data) port {:shutdown-timeout-seconds 1} (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 200) (.setConnectTimeoutMilliseconds 100) (Async/createClient))] (let [request-options (doto (RequestOptions. (str "http://localhost:" port "/hello")) (.setAs ResponseBodyType/UNBUFFERED_STREAM) (.setDecompressBody decompress-body?)) response (-> client (.get request-options) .deref) body (.getBody response) error (.getError response)] (is (nil? error)) ;; Consume the body to get the exception (is (thrown? SocketTimeoutException (slurp body)))))) (catch TimeoutException e ;; Expected whenever a server-side failure is generated nil))) (testing " - check connection timeout is handled" (with-open [client (-> (ClientOptions.) (.setConnectTimeoutMilliseconds 100) (Async/createClient))] (let [request-options (doto (RequestOptions. (str "http://localhost:" 12345 "/bad")) (.setAs ResponseBodyType/UNBUFFERED_STREAM) (.setDecompressBody decompress-body?)) response (-> client (.get request-options) .deref) error (.getError response)] (is error) (is (instance? ConnectException error)))))))) (deftest java-non-blocking-streaming-without-decompression (testing "java :unbuffered-stream with 32MB payload and no decompression" (java-non-blocking-streaming false))) (deftest java-non-blocking-streaming-with-decompression (testing "java :unbuffered-stream with 32MB payload and decompression" (java-non-blocking-streaming true))) (defn- java-blocking-streaming "Stream data that is buffered client-side i.e. in a blocking manner" [data response-body-type decompress-body?] (testlogging/with-test-logging (testing " - check data can be streamed successfully" (testwebserver/with-test-webserver-and-config (successful-handler data nil) port {:shutdown-timeout-seconds 1} (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 20000) (.setConnectTimeoutMilliseconds 100) (Async/createClient))] (let [request-options (doto (RequestOptions. (str "http://localhost:" port "/hello")) (.setAs response-body-type) (.setDecompressBody decompress-body?)) response (-> client (.get request-options) .deref) status (.getStatus response) body (.getBody response)] (is (= 200 status)) (let [instream body buf (make-array Byte/TYPE 4) _ (.read instream buf)] (is (= "xxxx" (String. buf "UTF-8"))) ;; Make sure we can read a few chars off of the stream (is (= (str data "yyyy") (str "xxxx" (slurp instream))))))))) ;; Read the rest and validate (testing " - check socket timeout is handled" (try (testwebserver/with-test-webserver-and-config (blocking-handler data) port {:shutdown-timeout-seconds 1} (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 200) (.setConnectTimeoutMilliseconds 100) (Async/createClient))] (let [request-options (doto (RequestOptions. (str "http://localhost:" port "/hello")) (.setAs response-body-type) (.setDecompressBody decompress-body?)) response (-> client (.get request-options) .deref) error (.getError response)] (is (instance? SocketTimeoutException error))))) (catch TimeoutException e ;; Expected whenever a server-side failure is generated nil))) (testing " - check connection timeout is handled" (with-open [client (-> (ClientOptions.) (.setConnectTimeoutMilliseconds 100) (Async/createClient))] (let [request-options (doto (RequestOptions. (str "http://localhost:" 12345 "/bad")) (.setAs response-body-type) (.setDecompressBody decompress-body?)) response (-> client (.get request-options) .deref) error (.getError response)] (is error) (is (instance? ConnectException error))))))) (deftest java-blocking-streaming-without-decompression (testing "java :unbuffered-stream with 1K payload and no decompression" ;; This is a small enough payload that :unbuffered-stream still buffers it all in memory and so it behaves ;; identically to :stream (java-blocking-streaming (generate-data 1024) ResponseBodyType/UNBUFFERED_STREAM false))) (deftest java-blocking-streaming-with-decompression (testing "java :unbuffered-stream with 1K payload and decompression" ;; This is a small enough payload that :unbuffered-stream still buffers it all in memory and so it behaves ;; identically to :stream (java-blocking-streaming (generate-data 1024) ResponseBodyType/UNBUFFERED_STREAM true))) (deftest java-existing-streaming-with-small-payload-without-decompression (testing "java :stream with 1K payload and no decompression" (java-blocking-streaming (generate-data 1024) ResponseBodyType/STREAM false))) (deftest java-existing-streaming-with-small-payload-with-decompression (testing "java :stream with 1K payload and decompression" (java-blocking-streaming (generate-data 1024) ResponseBodyType/STREAM true))) (deftest java-existing-streaming-with-large-payload-without-decompression (testing "java :stream with 32M payload and no decompression" (java-blocking-streaming (generate-data (* 32 1024 1024)) ResponseBodyType/STREAM false))) (deftest java-existing-streaming-with-large-payload-with-decompression (testing "java :stream with 32M payload and decompression" (java-blocking-streaming (generate-data (* 32 1024 1024)) ResponseBodyType/STREAM true))) clj-http-client-1.2.0/test/puppetlabs/http/client/gzip_request_test.clj000066400000000000000000000143661373223535200263770ustar00rootroot00000000000000(ns puppetlabs.http.client.gzip-request-test (:import (com.puppetlabs.http.client Sync SimpleRequestOptions ResponseBodyType CompressType) (java.io ByteArrayInputStream FilterInputStream) (java.net URI) (java.util.zip GZIPInputStream)) (:require [clojure.test :refer :all] [cheshire.core :as cheshire] [schema.test :as schema-test] [puppetlabs.http.client.sync :as http-client] [puppetlabs.http.client.test-common :refer :all] [puppetlabs.trapperkeeper.testutils.webserver :as testwebserver])) (use-fixtures :once schema-test/validate-schemas) (defn req-body-app [req] (let [response {:request-content-encoding (get-in req [:headers "content-encoding"]) :request-body-decompressed (slurp (GZIPInputStream. (:body req)) :encoding "utf-8")}] {:status 200 :headers {"Content-Type" "application/json; charset=utf-8"} :body (cheshire/generate-string response)})) (def short-request-body "gzip me�") (def big-request-body (apply str (repeat 4000 "and�i�said�hey�yeah�yeah�whats�going�on"))) (defn string->byte-array-input-stream [source is-closed-atom] (let [bis (-> source (.getBytes) (ByteArrayInputStream.))] (proxy [FilterInputStream] [bis] (close [] (reset! is-closed-atom true) (proxy-super close))))) (defn post-gzip-clj-request [port body] (-> (http-client/post (format "http://localhost:%d" port) {:body body :headers {"Content-Type" "text/plain; charset=utf-8"} :compress-request-body :gzip :as :text}) :body (cheshire/parse-string true))) (defn post-gzip-java-request [port body] (-> (SimpleRequestOptions. (URI. (format "http://localhost:%d/hello/" port))) (.setBody body) (.setHeaders {"Content-Type" "text/plain; charset=utf-8"}) (.setRequestBodyCompression CompressType/GZIP) (.setAs ResponseBodyType/TEXT) (Sync/post) (.getBody) (cheshire/parse-string true))) (deftest clj-sync-client-gzip-requests (testing "for clojure sync client" (testwebserver/with-test-webserver req-body-app port (testing "short string body is gzipped in request" (let [response (post-gzip-clj-request port short-request-body)] (is (= "gzip" (:request-content-encoding response))) (is (= short-request-body (:request-body-decompressed response))))) (testing "big string body is gzipped in request" (let [response (post-gzip-clj-request port big-request-body)] (is (= "gzip" (:request-content-encoding response))) (is (= big-request-body (:request-body-decompressed response))))) (testing "short inputstream body is gzipped in request" (let [is-closed (atom false) response (post-gzip-clj-request port (string->byte-array-input-stream short-request-body is-closed))] (is (= "gzip" (:request-content-encoding response))) (is (= short-request-body (:request-body-decompressed response))) (is @is-closed "input stream was not closed after request"))) (testing "big inputstream body is gzipped in request" (let [is-closed (atom false) response (post-gzip-clj-request port (string->byte-array-input-stream big-request-body is-closed))] (is (= "gzip" (:request-content-encoding response))) (is (= big-request-body (:request-body-decompressed response))) (is @is-closed "input stream was not closed after request")))))) (deftest java-sync-client-gzip-requests (testing "for java sync client" (testwebserver/with-test-webserver req-body-app port (testing "short string body is gzipped in request" (let [response (post-gzip-java-request port short-request-body)] (is (= "gzip" (:request-content-encoding response))) (is (= short-request-body (:request-body-decompressed response))))) (testing "big string body is gzipped in request" (let [response (post-gzip-java-request port big-request-body)] (is (= "gzip" (:request-content-encoding response))) (is (= big-request-body (:request-body-decompressed response))))) (testing "short inputstream body is gzipped in request" (let [is-closed (atom false) response (post-gzip-java-request port (string->byte-array-input-stream short-request-body is-closed))] (is (= "gzip" (:request-content-encoding response))) (is (= short-request-body (:request-body-decompressed response))) (is @is-closed "input stream was not closed after request"))) (testing "big inputstream body is gzipped in request" (let [is-closed (atom false) response (post-gzip-java-request port (string->byte-array-input-stream big-request-body is-closed))] (is (= "gzip" (:request-content-encoding response))) (is (= big-request-body (:request-body-decompressed response))) (is @is-closed "input stream was not closed after request")))))) (deftest connect-exception-during-gzip-request-returns-failure (testing "connection exception during gzip request returns failure" (let [is-closed (atom false)] (is (connect-exception-thrown? (http-client/post "http://localhost:65535" {:body (string->byte-array-input-stream short-request-body is-closed) :compress-request-body :gzip :as :text}))) (is @is-closed "input stream was not closed after request")))) clj-http-client-1.2.0/test/puppetlabs/http/client/metrics_test.clj000066400000000000000000001424461373223535200253250ustar00rootroot00000000000000(ns puppetlabs.http.client.metrics-test (:require [clojure.test :refer :all] [puppetlabs.http.client.async-unbuffered-test :as unbuffered-test] [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9] [puppetlabs.trapperkeeper.testutils.bootstrap :as testutils] [puppetlabs.trapperkeeper.testutils.logging :as testlogging] [puppetlabs.trapperkeeper.testutils.webserver :as testwebserver] [puppetlabs.http.client.async :as async] [puppetlabs.http.client.sync :as sync] [puppetlabs.http.client.common :as common] [puppetlabs.trapperkeeper.core :as tk] [puppetlabs.http.client.metrics :as metrics] [schema.test :as schema-test]) (:import (com.puppetlabs.http.client Async RequestOptions ClientOptions ResponseBodyType Sync) (com.codahale.metrics MetricRegistry) (java.net SocketTimeoutException) (java.util.concurrent TimeoutException) (com.puppetlabs.http.client.metrics Metrics ClientTimer ClientMetricData))) (use-fixtures :once schema-test/validate-schemas) (def metric-namespace "puppetlabs.http-client.experimental") (tk/defservice test-metric-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler (fn [_] {:status 200 :body "Hello, World!"}) "/hello") (add-ring-handler (fn [_] (do (Thread/sleep 5) {:status 200 :body "short"})) "/short") (add-ring-handler (fn [_] (do (Thread/sleep 100) {:status 200 :body "long"})) "/long") context)) (def hello-url "http://localhost:10000/hello") (def short-url "http://localhost:10000/short") (def long-url "http://localhost:10000/long") (def short-name (format "%s.with-url.%s.full-response" metric-namespace short-url)) (def short-name-with-get (format "%s.with-url-and-method.%s.GET.full-response" metric-namespace short-url)) (def short-name-with-post (format "%s.with-url-and-method.%s.POST.full-response" metric-namespace short-url)) (def long-name (format "%s.with-url.%s.full-response" metric-namespace long-url)) (def long-name-with-method (format "%s.with-url-and-method.%s.GET.full-response" metric-namespace long-url)) (def long-foo-name "puppetlabs.http-client.experimental.with-metric-id.foo.full-response") (def long-foo-bar-name "puppetlabs.http-client.experimental.with-metric-id.foo.bar.full-response") (def long-foo-bar-baz-name "puppetlabs.http-client.experimental.with-metric-id.foo.bar.baz.full-response") (def hello-name (format "%s.with-url.%s.full-response" metric-namespace hello-url)) (def hello-name-with-method (format "%s.with-url-and-method.%s.GET.full-response" metric-namespace hello-url)) (deftest metrics-test-java-async (testing "metrics work with java async client" (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-metric-web-service] {:webserver {:port 10000}} (let [metric-registry (MetricRegistry.) hello-request-opts (RequestOptions. hello-url) short-request-opts (RequestOptions. short-url) long-request-opts (doto (RequestOptions. long-url) (.setMetricId (into-array ["foo" "bar" "baz"])))] (with-open [client (Async/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry)))] (-> client (.get hello-request-opts) (.deref)) ; warm it up (let [short-response (-> client (.get short-request-opts) (.deref)) long-response (-> client (.get long-request-opts) (.deref))] (-> client (.post short-request-opts) (.deref)) (is (= 200 (.getStatus short-response))) (is (= "short" (slurp (.getBody short-response)))) (is (= 200 (.getStatus long-response))) (is (= "long" (slurp (.getBody long-response)))) (.timer metric-registry "fake") (let [client-metric-registry (.getMetricRegistry client) client-metrics (Metrics/getClientMetrics client-metric-registry) client-metrics-data (Metrics/getClientMetricsData client-metric-registry) url-metrics (.getUrlTimers client-metrics) url-and-method-metrics (.getUrlAndMethodTimers client-metrics) metric-id-metrics (.getMetricIdTimers client-metrics) url-metrics-data (.getUrlData client-metrics-data) url-and-method-metrics-data (.getUrlAndMethodData client-metrics-data) metric-id-metrics-data (.getMetricIdData client-metrics-data) all-metrics (.getMetrics metric-registry)] (testing ".getMetricRegistry returns the associated MetricRegistry" (is (instance? MetricRegistry client-metric-registry))) (testing "Metrics/getClientMetrics returns only http client metrics" (is (= 11 (count all-metrics))) (is (= 10 (+ (count url-metrics) (count url-and-method-metrics) (count metric-id-metrics)))) (is (= 10 (+ (count url-metrics-data) (count url-and-method-metrics-data) (count metric-id-metrics-data))))) (testing ".getClientMetrics returns a map of category to array of timers" (is (= (set (list hello-name short-name long-name)) (set (map #(.getMetricName %) url-metrics)) (set (map #(.getMetricName %) url-metrics-data)))) (is (= (set (list hello-name-with-method short-name-with-get short-name-with-post long-name-with-method)) (set (map #(.getMetricName %) url-and-method-metrics)) (set (map #(.getMetricName %) url-and-method-metrics-data)))) (is (= (set (list long-foo-name long-foo-bar-name long-foo-bar-baz-name)) (set (map #(.getMetricName %) metric-id-metrics)) (set (map #(.getMetricName %) metric-id-metrics-data)))) (is (every? #(instance? ClientTimer %) url-metrics)) (is (every? #(instance? ClientTimer %) url-and-method-metrics)) (is (every? #(instance? ClientTimer %) metric-id-metrics))) (testing ".getClientMetricsData returns a map of metric category to arrays of metric data" (let [short-data (first (filter #(= short-name (.getMetricName %)) url-metrics-data)) short-data-get (first (filter #(= short-name-with-get (.getMetricName %)) url-and-method-metrics-data)) short-data-post (first (filter #(= short-name-with-post (.getMetricName %)) url-and-method-metrics-data)) long-data (first (filter #(= long-name (.getMetricName %)) url-metrics-data))] (is (every? #(instance? ClientMetricData %) (concat url-metrics-data url-and-method-metrics-data metric-id-metrics-data))) (is (= short-name (.getMetricName short-data))) (is (= 2 (.getCount short-data))) (is (<= 5 (.getMean short-data))) (is (<= 10 (.getAggregate short-data))) (is (= short-name-with-get (.getMetricName short-data-get))) (is (= 1 (.getCount short-data-get))) (is (<= 5 (.getMean short-data-get))) (is (<= 5 (.getAggregate short-data-get))) (is (= short-name-with-post (.getMetricName short-data-post))) (is (= 1 (.getCount short-data-post))) (is (<= 5 (.getMean short-data-post))) (is (<= 5 (.getAggregate short-data-post))) (is (>= 1 (Math/abs (- (.getAggregate short-data) (+ (.getAggregate short-data-get) (.getAggregate short-data-post)))))) (is (= long-name (.getMetricName long-data))) (is (= 1 (.getCount long-data))) (is (<= 100 (.getMean long-data))) (is (<= 100 (.getAggregate long-data))) (is (> (.getAggregate long-data) (.getAggregate short-data)))))))) (with-open [client (Async/createClient (ClientOptions.))] (testing ".getMetricRegistry returns nil if no metric registry passed in" (is (= nil (.getMetricRegistry client)))))))))) (deftest metrics-test-clojure-async (testing "metrics work with clojure async client" (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-metric-web-service] {:webserver {:port 10000}} (let [metric-registry (MetricRegistry.)] (with-open [client (async/create-client {:metric-registry metric-registry})] @(common/get client hello-url) ; warm it up (let [short-response @(common/get client short-url {:as :text :metric-id ["foo" "bar" "baz"]}) long-response @(common/get client long-url)] @(common/post client short-url) (is (= {:status 200 :body "short"} (select-keys short-response [:status :body]))) (is (= 200 (:status long-response))) (is (= "long" (slurp (:body long-response)))) (.timer metric-registry "fake") (let [client-metric-registry (common/get-client-metric-registry client) client-metrics (metrics/get-client-metrics client-metric-registry) client-metrics-data (metrics/get-client-metrics-data client-metric-registry) url-metrics (:url client-metrics) url-and-method-metrics (:url-and-method client-metrics) metric-id-metrics (:metric-id client-metrics) url-metrics-data (:url client-metrics-data) url-and-method-metrics-data (:url-and-method client-metrics-data) metric-id-metrics-data (:metric-id client-metrics-data) all-metrics (.getMetrics metric-registry)] (testing "get-client-metric-registry returns the associated MetricRegistry" (is (instance? MetricRegistry client-metric-registry))) (testing "get-client-metrics and get-client-metrics data return only http client metrics" (is (= 11 (count all-metrics))) (is (= 10 (+ (count url-metrics) (count url-and-method-metrics) (count metric-id-metrics)))) (is (= 10 (+ (count url-metrics-data) (count url-and-method-metrics-data) (count metric-id-metrics-data))))) (testing "get-client-metrics returns a map of category to array of timers" (is (= (set (list hello-name short-name long-name)) (set (map #(.getMetricName %) url-metrics)) (set (map :metric-name url-metrics-data)))) (is (= (set (list hello-name-with-method short-name-with-get short-name-with-post long-name-with-method)) (set (map #(.getMetricName %) url-and-method-metrics)) (set (map :metric-name url-and-method-metrics-data)))) (is (= (set (list long-foo-name long-foo-bar-name long-foo-bar-baz-name)) (set (map #(.getMetricName %) metric-id-metrics)) (set (map :metric-name metric-id-metrics-data)))) (is (every? #(instance? ClientTimer %) url-metrics)) (is (every? #(instance? ClientTimer %) url-and-method-metrics)) (is (every? #(instance? ClientTimer %) metric-id-metrics))) (testing "get-client-metrics-data returns a map of metric category to metric data" (let [short-data (first (filter #(= short-name (:metric-name %)) url-metrics-data)) short-data-get (first (filter #(= short-name-with-get (:metric-name %)) url-and-method-metrics-data)) short-data-post (first (filter #(= short-name-with-post (:metric-name %)) url-and-method-metrics-data)) long-data (first (filter #(= long-name (:metric-name %)) url-metrics-data))] (is (= short-name (:metric-name short-data))) (is (= 2 (:count short-data))) (is (<= 5 (:mean short-data))) (is (<= 10 (:aggregate short-data))) (is (= short-name-with-get (:metric-name short-data-get))) (is (= 1 (:count short-data-get))) (is (<= 5 (:mean short-data-get))) (is (<= 5 (:aggregate short-data-get))) (is (= short-name-with-post (:metric-name short-data-post))) (is (= 1 (:count short-data-post))) (is (<= 5 (:mean short-data-post))) (is (<= 5 (:aggregate short-data-post))) (is (>= 1 (Math/abs (- (:aggregate short-data) (+ (:aggregate short-data-get) (:aggregate short-data-post)))))) (is (= long-name (:metric-name long-data))) (is (= 1 (:count long-data))) (is (<= 100 (:mean long-data))) (is (<= 100 (:aggregate long-data))) (is (> (:mean long-data) (:mean short-data))))))))) (with-open [client (async/create-client {})] (testing "get-client-metric-registry returns nil if no metric registry passed in" (is (= nil (common/get-client-metric-registry client))))))))) (deftest metrics-test-java-sync (testing "metrics work with java sync client" (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-metric-web-service] {:webserver {:port 10000}} (let [metric-registry (MetricRegistry.) hello-request-opts (RequestOptions. hello-url) short-request-opts (RequestOptions. short-url) long-request-opts (doto (RequestOptions. long-url) (.setMetricId (into-array ["foo" "bar" "baz"])))] (with-open [client (Sync/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry)))] (.get client hello-request-opts) ; warm it up (let [short-response (.get client short-request-opts) long-response (.get client long-request-opts)] (.post client short-request-opts) (is (= 200 (.getStatus short-response))) (is (= "short" (slurp (.getBody short-response)))) (is (= 200 (.getStatus long-response))) (is (= "long" (slurp (.getBody long-response)))) (.timer metric-registry "fake") (let [client-metric-registry (.getMetricRegistry client) client-metrics (Metrics/getClientMetrics client-metric-registry) client-metrics-data (Metrics/getClientMetricsData client-metric-registry) url-metrics (.getUrlTimers client-metrics) url-and-method-metrics (.getUrlAndMethodTimers client-metrics) metric-id-metrics (.getMetricIdTimers client-metrics) url-metrics-data (.getUrlData client-metrics-data) url-and-method-metrics-data (.getUrlAndMethodData client-metrics-data) metric-id-metrics-data (.getMetricIdData client-metrics-data) all-metrics (.getMetrics metric-registry)] (testing ".getMetricRegistry returns the associated MetricRegistry" (is (instance? MetricRegistry client-metric-registry))) (testing "Metrics/getClientMetrics returns only http client metrics" (is (= 11 (count all-metrics))) (is (= 10 (+ (count url-metrics) (count url-and-method-metrics) (count metric-id-metrics)))) (is (= 10 (+ (count url-metrics-data) (count url-and-method-metrics-data) (count metric-id-metrics-data))))) (testing ".getClientMetrics returns a map of category to array of timers" (is (= (set (list hello-name short-name long-name)) (set (map #(.getMetricName %) url-metrics)) (set (map #(.getMetricName %) url-metrics-data)))) (is (= (set (list hello-name-with-method short-name-with-get short-name-with-post long-name-with-method)) (set (map #(.getMetricName %) url-and-method-metrics)) (set (map #(.getMetricName %) url-and-method-metrics-data)))) (is (= (set (list long-foo-name long-foo-bar-name long-foo-bar-baz-name)) (set (map #(.getMetricName %) metric-id-metrics)) (set (map #(.getMetricName %) metric-id-metrics-data)))) (is (every? #(instance? ClientTimer %) url-metrics)) (is (every? #(instance? ClientTimer %) url-and-method-metrics)) (is (every? #(instance? ClientTimer %) metric-id-metrics))) (testing ".getClientMetricsData returns a map of metric category to arrays of metric data" (let [short-data (first (filter #(= short-name (.getMetricName %)) url-metrics-data)) short-data-get (first (filter #(= short-name-with-get (.getMetricName %)) url-and-method-metrics-data)) short-data-post (first (filter #(= short-name-with-post (.getMetricName %)) url-and-method-metrics-data)) long-data (first (filter #(= long-name (.getMetricName %)) url-metrics-data))] (is (every? #(instance? ClientMetricData %) (concat url-metrics-data url-and-method-metrics-data metric-id-metrics-data))) (is (= short-name (.getMetricName short-data))) (is (= 2 (.getCount short-data))) (is (<= 5 (.getMean short-data))) (is (<= 10 (.getAggregate short-data))) (is (= short-name-with-get (.getMetricName short-data-get))) (is (= 1 (.getCount short-data-get))) (is (<= 5 (.getMean short-data-get))) (is (<= 5 (.getAggregate short-data-get))) (is (= short-name-with-post (.getMetricName short-data-post))) (is (= 1 (.getCount short-data-post))) (is (<= 5 (.getMean short-data-post))) (is (<= 5 (.getAggregate short-data-post))) (is (>= 1 (Math/abs (- (.getAggregate short-data) (+ (.getAggregate short-data-get) (.getAggregate short-data-post)))))) (is (= long-name (.getMetricName long-data))) (is (= 1 (.getCount long-data))) (is (<= 100 (.getMean long-data))) (is (<= 100 (.getAggregate long-data))) (is (> (.getMean long-data) (.getMean short-data)))))))) (with-open [client (Sync/createClient (ClientOptions.))] (testing ".getMetricRegistry returns nil if no metric registry passed in" (is (= nil (.getMetricRegistry client)))))))))) (deftest metrics-test-clojure-sync (testing "metrics work with clojure sync client" (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-metric-web-service] {:webserver {:port 10000}} (let [metric-registry (MetricRegistry.)] (with-open [client (sync/create-client {:metric-registry metric-registry})] (common/get client hello-url) ; warm it up (let [short-response (common/get client short-url {:as :text}) long-response (common/get client long-url {:as :text :metric-id ["foo" "bar" "baz"]})] (common/post client short-url) (is (= {:status 200 :body "short"} (select-keys short-response [:status :body]))) (is (= {:status 200 :body "long"} (select-keys long-response [:status :body]))) (.timer metric-registry "fake") (let [client-metric-registry (common/get-client-metric-registry client) client-metrics (metrics/get-client-metrics client-metric-registry) client-metrics-data (metrics/get-client-metrics-data client-metric-registry) url-metrics (:url client-metrics) url-and-method-metrics (:url-and-method client-metrics) metric-id-metrics (:metric-id client-metrics) url-metrics-data (:url client-metrics-data) url-and-method-metrics-data (:url-and-method client-metrics-data) metric-id-metrics-data (:metric-id client-metrics-data) all-metrics (.getMetrics metric-registry)] (testing "get-client-metric-registry returns the associated MetricRegistry" (is (instance? MetricRegistry client-metric-registry))) (testing "get-client-metrics and get-client-metrics data return only http client metrics" (is (= 11 (count all-metrics))) (is (= 10 (+ (count url-metrics) (count url-and-method-metrics) (count metric-id-metrics)))) (is (= 10 (+ (count url-metrics-data) (count url-and-method-metrics-data) (count metric-id-metrics-data))))) (testing "get-client-metrics returns a map of category to array of timers" (is (= (set (list hello-name short-name long-name)) (set (map #(.getMetricName %) url-metrics)) (set (map :metric-name url-metrics-data)))) (is (= (set (list hello-name-with-method short-name-with-get short-name-with-post long-name-with-method)) (set (map #(.getMetricName %) url-and-method-metrics)) (set (map :metric-name url-and-method-metrics-data)))) (is (= (set (list long-foo-name long-foo-bar-name long-foo-bar-baz-name)) (set (map #(.getMetricName %) metric-id-metrics)) (set (map :metric-name metric-id-metrics-data)))) (is (every? #(instance? ClientTimer %) url-metrics)) (is (every? #(instance? ClientTimer %) url-and-method-metrics)) (is (every? #(instance? ClientTimer %) metric-id-metrics))) (testing "get-client-metrics-data returns a map of metric category to metric data" (let [short-data (first (filter #(= short-name (:metric-name %)) url-metrics-data)) short-data-get (first (filter #(= short-name-with-get (:metric-name %)) url-and-method-metrics-data)) short-data-post (first (filter #(= short-name-with-post (:metric-name %)) url-and-method-metrics-data)) long-data (first (filter #(= long-name (:metric-name %)) url-metrics-data))] (is (= short-name (:metric-name short-data))) (is (= 2 (:count short-data))) (is (<= 5 (:mean short-data))) (is (<= 10 (:aggregate short-data))) (is (= short-name-with-get (:metric-name short-data-get))) (is (= 1 (:count short-data-get))) (is (<= 5 (:mean short-data-get))) (is (<= 5 (:aggregate short-data-get))) (is (= short-name-with-post (:metric-name short-data-post))) (is (= 1 (:count short-data-post))) (is (<= 5 (:mean short-data-post))) (is (<= 5 (:aggregate short-data-post))) (is (>= 1 (Math/abs (- (:aggregate short-data) (+ (:aggregate short-data-get) (:aggregate short-data-post)))))) (is (= long-name (:metric-name long-data))) (is (= 1 (:count long-data))) (is (<= 100 (:mean long-data))) (is (<= 100 (:aggregate long-data))) (is (> (:mean long-data) (:mean short-data)))))))) (with-open [client (sync/create-client {})] (testing "get-client-metric-registry returns nil if no metric registry passed in" (is (= nil (common/get-client-metric-registry client)))))))))) (deftest java-metrics-for-unbuffered-streaming-test (testlogging/with-test-logging (let [data (unbuffered-test/generate-data (* 1024 1024))] (testing "metrics work for a successful request" (let [metric-registry (MetricRegistry.)] (testwebserver/with-test-webserver-and-config (unbuffered-test/successful-handler data nil) port {:shutdown-timeout-seconds 1} (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 20000) (.setConnectTimeoutMilliseconds 100) (.setMetricRegistry metric-registry) (Async/createClient))] (let [url (str "http://localhost:" port "/hello") request-options (doto (RequestOptions. url) (.setAs ResponseBodyType/UNBUFFERED_STREAM)) response (-> client (.get request-options) .deref) status (.getStatus response) body (.getBody response)] (is (= 200 status)) (let [instream body buf (make-array Byte/TYPE 4)] (.read instream buf) (is (= "xxxx" (String. buf "UTF-8"))) ;; Make sure we can read a few chars off of the stream (Thread/sleep 1000) ;; check that the full-response metric takes this into account (is (= (str data "yyyy") (str "xxxx" (slurp instream))))) ;; Read the rest and validate (let [client-metric-registry (.getMetricRegistry client) client-metrics (Metrics/getClientMetrics client-metric-registry) client-metrics-data (Metrics/getClientMetricsData client-metric-registry) full-response-name (format "%s.with-url.%s.full-response" metric-namespace url) full-response-name-with-method (format "%s.with-url-and-method.%s.GET.full-response" metric-namespace url)] (is (= [full-response-name] (map #(.getMetricName %) (.getUrlTimers client-metrics)) (map #(.getMetricName %) (.getUrlData client-metrics-data)))) (is (= [full-response-name-with-method] (map #(.getMetricName %) (.getUrlAndMethodTimers client-metrics)) (map #(.getMetricName %) (.getUrlAndMethodData client-metrics-data)))) (is (= [] (.getMetricIdTimers client-metrics) (.getMetricIdData client-metrics-data))) (is (every? #(instance? ClientTimer %) (concat (.getUrlTimers client-metrics) (.getUrlAndMethodTimers client-metrics)))) (let [full-response-data (first (.getUrlData client-metrics-data))] (is (every? #(instance? ClientMetricData %) (concat (.getUrlData client-metrics-data) (.getUrlAndMethodData client-metrics-data)))) (is (= 1 (.getCount full-response-data))) (is (= full-response-name (.getMetricName full-response-data))) (is (<= 1000 (.getMean full-response-data))) (is (<= 1000 (.getAggregate full-response-data)))))))))) (testing "metrics work for failed request" (try (testwebserver/with-test-webserver-and-config (unbuffered-test/blocking-handler data) port {:shutdown-timeout-seconds 1} (let [metric-registry (MetricRegistry.)] (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 200) (.setConnectTimeoutMilliseconds 100) (.setMetricRegistry metric-registry) (Async/createClient))] (let [url (str "http://localhost:" port "/hello") request-options (doto (RequestOptions. url) (.setAs ResponseBodyType/UNBUFFERED_STREAM)) response (-> client (.get request-options) .deref) error (.getError response) body (.getBody response)] (is (nil? error)) (is (thrown? SocketTimeoutException (slurp body))) (let [client-metric-registry (.getMetricRegistry client) client-metrics (Metrics/getClientMetrics client-metric-registry) client-metrics-data (Metrics/getClientMetricsData client-metric-registry) full-response-name (format "%s.with-url.%s.full-response" metric-namespace url) full-response-name-with-method (format "%s.with-url-and-method.%s.GET.full-response" metric-namespace url)] (is (= [full-response-name] (map #(.getMetricName %) (.getUrlTimers client-metrics)) (map #(.getMetricName %) (.getUrlData client-metrics-data)))) (is (= [full-response-name-with-method] (map #(.getMetricName %) (.getUrlAndMethodTimers client-metrics)) (map #(.getMetricName %) (.getUrlAndMethodData client-metrics-data)))) (is (= [] (.getMetricIdTimers client-metrics) (.getMetricIdData client-metrics-data))) (is (every? #(instance? ClientTimer %) (concat (.getUrlTimers client-metrics) (.getUrlAndMethodTimers client-metrics)))) (let [full-response-data (first (.getUrlData client-metrics-data))] (is (every? #(instance? ClientMetricData %) (concat (.getUrlData client-metrics-data) (.getUrlAndMethodData client-metrics-data)))) (is (= 1 (.getCount full-response-data))) (is (= full-response-name (.getMetricName full-response-data))) (is (<= 200 (.getMean full-response-data))) (is (<= 200 (.getAggregate full-response-data))))))))) (catch TimeoutException e ;; Expected whenever a server-side failure is generated )))))) (deftest clojure-metrics-for-unbuffered-streaming-test (testlogging/with-test-logging (let [data (unbuffered-test/generate-data (* 1024 1024)) opts {:as :unbuffered-stream}] (testing "metrics work for a successful request" (let [metric-registry (MetricRegistry.)] (testwebserver/with-test-webserver-and-config (unbuffered-test/successful-handler data nil) port {:shutdown-timeout-seconds 1} (with-open [client (async/create-client {:connect-timeout-milliseconds 100 :socket-timeout-milliseconds 20000 :metric-registry metric-registry})] (let [url (str "http://localhost:" port "/hello") response @(common/get client url opts) {:keys [status body]} response] (is (= 200 status)) (let [instream body buf (make-array Byte/TYPE 4)] (.read instream buf) (is (= "xxxx" (String. buf "UTF-8"))) ;; Make sure we can read a few chars off of the stream (Thread/sleep 1000) ;; check that the full-response metric takes this into account (is (= (str data "yyyy") (str "xxxx" (slurp instream))))) ;; Read the rest and validate (let [client-metric-registry (common/get-client-metric-registry client) client-metrics (metrics/get-client-metrics client-metric-registry) client-metrics-data (metrics/get-client-metrics-data client-metric-registry) full-response-name (format "%s.with-url.%s.full-response" metric-namespace url) full-response-name-with-method (format "%s.with-url-and-method.%s.GET.full-response" metric-namespace url)] (is (= [full-response-name] (map #(.getMetricName %) (:url client-metrics)) (map :metric-name (:url client-metrics-data)))) (is (= [full-response-name-with-method] (map #(.getMetricName %) (:url-and-method client-metrics)) (map :metric-name (:url-and-method client-metrics-data)))) (is (= [] (:metric-id client-metrics) (:metric-id client-metrics-data))) (is (every? #(instance? ClientTimer %) (concat (:url client-metrics) (:url-and-method client-metrics)))) (let [full-response-data (first (:url client-metrics-data))] (is (= {:count 1 :metric-name full-response-name} (select-keys full-response-data [:metric-name :count]))) (is (<= 1000 (:mean full-response-data))) (is (<= 1000 (:aggregate full-response-data)))))))))) (testing "metrics work for a failed request" (try (testwebserver/with-test-webserver-and-config (unbuffered-test/blocking-handler data) port {:shutdown-timeout-seconds 1} (let [metric-registry (MetricRegistry.) url (str "http://localhost:" port "/hello")] (with-open [client (async/create-client {:connect-timeout-milliseconds 100 :socket-timeout-milliseconds 200 :metric-registry metric-registry})] (let [response @(common/get client url opts) {:keys [body error]} response] (is (nil? error)) ;; Consume the body to get the exception (is (thrown? SocketTimeoutException (slurp body)))) (let [client-metric-registry (common/get-client-metric-registry client) client-metrics (metrics/get-client-metrics client-metric-registry) client-metrics-data (metrics/get-client-metrics-data client-metric-registry) full-response-name (format "%s.with-url.%s.full-response" metric-namespace url) full-response-name-with-method (format "%s.with-url-and-method.%s.GET.full-response" metric-namespace url)] (is (= [full-response-name] (map #(.getMetricName %) (:url client-metrics)) (map :metric-name (:url client-metrics-data)))) (is (= [full-response-name-with-method] (map #(.getMetricName %) (:url-and-method client-metrics)) (map :metric-name (:url-and-method client-metrics-data)))) (is (= [] (:metric-id client-metrics) (:metric-id client-metrics-data))) (is (every? #(instance? ClientTimer %) (concat (:url client-metrics) (:url-and-method client-metrics)))) (let [full-response-data (first (:url client-metrics-data))] (is (= {:count 1 :metric-name full-response-name} (select-keys full-response-data [:metric-name :count]))) (is (<= 200 (:mean full-response-data))) (is (<= 200 (:aggregate full-response-data)))))))) (catch TimeoutException e ;; Expected whenever a server-side failure is generated )))))) (deftest metric-namespace-test (let [metric-prefix "my-metric-prefix" server-id "my-server" metric-name-with-prefix (format "%s.http-client.experimental.with-url.%s.full-response" metric-prefix hello-url) metric-name-with-server-id (format "puppetlabs.%s.http-client.experimental.with-url.%s.full-response" server-id hello-url) get-metric-name (fn [metric-registry] (.getMetricName (first (Metrics/getClientMetricsDataByUrl metric-registry hello-url))))] (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-metric-web-service] {:webserver {:port 10000}} (testing "custom metric namespace works for java async client" (testing "metric prefix works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-metric-prefix (Async/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry) (.setMetricPrefix metric-prefix)))] (is (= (format "%s.http-client.experimental" metric-prefix) (.getMetricNamespace client-with-metric-prefix))) (-> client-with-metric-prefix (.get (RequestOptions. hello-url))) (is (= metric-name-with-prefix (get-metric-name metric-registry)))))) (testing "server id works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (Async/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry) (.setServerId server-id)))] (-> client-with-server-id (.get (RequestOptions. hello-url))) (is (= metric-name-with-server-id (get-metric-name metric-registry)))))) (testing "metric prefix overrides server id if both are set" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (Async/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry) (.setMetricPrefix metric-prefix) (.setServerId server-id)))] (-> client-with-server-id (.get (RequestOptions. hello-url))) (is (= metric-name-with-prefix (get-metric-name metric-registry))) (is (logged? #"Metric prefix and server id both set.*" :warn)))))) (testing "custom metric namespace works for clojure async client" (testing "metric prefix works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-metric-prefix (async/create-client {:metric-registry metric-registry :metric-prefix metric-prefix})] (is (= (format "%s.http-client.experimental" metric-prefix) (common/get-client-metric-namespace client-with-metric-prefix))) (-> client-with-metric-prefix (common/get hello-url)) (is (= metric-name-with-prefix (get-metric-name metric-registry)))))) (testing "server id works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (async/create-client {:metric-registry metric-registry :server-id server-id})] (-> client-with-server-id (common/get hello-url)) (is (= metric-name-with-server-id (get-metric-name metric-registry)))))) (testing "metric prefix overrides server id if both are set" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (async/create-client {:metric-registry metric-registry :metric-prefix metric-prefix :server-id server-id})] (-> client-with-server-id (common/get hello-url)) (is (= metric-name-with-prefix (get-metric-name metric-registry))))))) (testing "custom metric namespace works for Java sync client" (testing "metric prefix works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-metric-prefix (Sync/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry) (.setMetricPrefix metric-prefix)))] (is (= (format "%s.http-client.experimental" metric-prefix) (.getMetricNamespace client-with-metric-prefix))) (-> client-with-metric-prefix (.get (RequestOptions. hello-url))) (is (= metric-name-with-prefix (get-metric-name metric-registry)))))) (testing "server id works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (Sync/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry) (.setServerId server-id)))] (-> client-with-server-id (.get (RequestOptions. hello-url))) (is (= metric-name-with-server-id (get-metric-name metric-registry)))))) (testing "metric prefix overrides server id if both are set" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (Sync/createClient (doto (ClientOptions.) (.setMetricRegistry metric-registry) (.setMetricPrefix metric-prefix) (.setServerId server-id)))] (-> client-with-server-id (.get (RequestOptions. hello-url))) (is (= metric-name-with-prefix (get-metric-name metric-registry))))))) (testing "custom metric namespace works for clojure sync client" (testing "metric prefix works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-metric-prefix (sync/create-client {:metric-registry metric-registry :metric-prefix metric-prefix})] (is (= (format "%s.http-client.experimental" metric-prefix) (common/get-client-metric-namespace client-with-metric-prefix))) (-> client-with-metric-prefix (common/get hello-url)) (is (= metric-name-with-prefix (get-metric-name metric-registry)))))) (testing "server id works" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (sync/create-client {:metric-registry metric-registry :server-id server-id})] (-> client-with-server-id (common/get hello-url)) (is (= metric-name-with-server-id (get-metric-name metric-registry)))))) (testing "metric prefix overrides server id if both are set" (let [metric-registry (MetricRegistry.)] (with-open [client-with-server-id (sync/create-client {:metric-registry metric-registry :metric-prefix metric-prefix :server-id server-id})] (-> client-with-server-id (common/get hello-url)) (is (= metric-name-with-prefix (get-metric-name metric-registry))))))))))) (deftest toggleable-url-metrics (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-metric-web-service] {:webserver {:port 10000}} (testing "url-metrics can be disabled for clojure async client" (with-open [client (async/create-client {:metric-registry (MetricRegistry.) :enable-url-metrics? false})] (let [response @(common/get client hello-url {:metric-id ["foo" "bar"]})] (is (= 200 (:status response))) (let [client-metrics (-> client (common/get-client-metric-registry) (metrics/get-client-metrics)) metric-names (set (map #(.getMetricName %) (:metric-id client-metrics)))] (is (empty? (:url client-metrics))) (is (empty? (:url-and-method client-metrics))) (is (= #{long-foo-name long-foo-bar-name} metric-names)))))) (testing "url-metrics can be disabled for java async client" (with-open [client (Async/createClient (doto (ClientOptions.) (.setMetricRegistry (MetricRegistry.)) (.setEnableURLMetrics false)))] (let [request-opts (doto (RequestOptions. hello-url) (.setMetricId (into-array ["foo" "bar"]))) response (-> client (.get request-opts) (.deref))] (is (= 200 (.getStatus response))) (let [client-metrics (-> client (.getMetricRegistry) (Metrics/getClientMetrics)) metric-names (set (map #(.getMetricName %) (.getMetricIdTimers client-metrics)))] (is (.isEmpty (.getUrlTimers client-metrics))) (is (.isEmpty (.getUrlAndMethodTimers client-metrics))) (is (= #{long-foo-name long-foo-bar-name} metric-names)))))) (testing "url-metrics can be disabled for clojure sync client" (with-open [client (sync/create-client {:metric-registry (MetricRegistry.) :enable-url-metrics? false})] (let [response (common/get client hello-url {:metric-id ["foo" "bar"]})] (is (= 200 (:status response))) (let [client-metrics (-> client (common/get-client-metric-registry) (metrics/get-client-metrics)) metric-names (set (map #(.getMetricName %) (:metric-id client-metrics)))] (is (empty? (:url client-metrics))) (is (empty? (:url-and-method client-metrics))) (is (= #{long-foo-name long-foo-bar-name} metric-names)))))) (testing "url-metrics can be disabled for java sync client" (with-open [client (Sync/createClient (doto (ClientOptions.) (.setMetricRegistry (MetricRegistry.)) (.setEnableURLMetrics false)))] (let [request-opts (doto (RequestOptions. hello-url) (.setMetricId (into-array ["foo" "bar"]))) response (-> client (.get request-opts))] (is (= 200 (.getStatus response))) (let [client-metrics (-> client (.getMetricRegistry) (Metrics/getClientMetrics)) metric-names (set (map #(.getMetricName %) (.getMetricIdTimers client-metrics)))] (is (.isEmpty (.getUrlTimers client-metrics))) (is (.isEmpty (.getUrlAndMethodTimers client-metrics))) (is (= #{long-foo-name long-foo-bar-name} metric-names)))))))))clj-http-client-1.2.0/test/puppetlabs/http/client/sync_plaintext_test.clj000066400000000000000000001155271373223535200267230ustar00rootroot00000000000000(ns puppetlabs.http.client.sync-plaintext-test (:import (com.puppetlabs.http.client Sync RequestOptions SimpleRequestOptions ResponseBodyType ClientOptions HttpClientException) (java.io ByteArrayInputStream InputStream) (org.apache.http.impl.nio.client HttpAsyncClients) (java.net ConnectException ServerSocket SocketTimeoutException URI)) (:require [clojure.test :refer :all] [puppetlabs.http.client.test-common :refer :all] [puppetlabs.trapperkeeper.core :as tk] [puppetlabs.trapperkeeper.testutils.bootstrap :as testutils] [puppetlabs.trapperkeeper.testutils.logging :as testlogging] [puppetlabs.trapperkeeper.testutils.webserver :as testwebserver] [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9] [puppetlabs.http.client.sync :as sync] [puppetlabs.http.client.common :as common] [schema.test :as schema-test] [clojure.java.io :as io] [ring.middleware.cookies :refer [wrap-cookies]])) (use-fixtures :once schema-test/validate-schemas) (defn app [_] {:status 200 :body "Hello, World!"}) (defn cookie-handler [_] {:status 200 :body "cookie has been set" :cookies {"session_id" {:value "session-id-hash"}}}) (defn check-cookie-handler [req] (if (empty? (get req :cookies)) {:status 400 :body "cookie has not been set"} {:status 200 :body "cookie has been set"})) (tk/defservice test-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler app "/hello") context)) (tk/defservice test-cookie-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler (wrap-cookies cookie-handler) "/cookietest") (add-ring-handler (wrap-cookies check-cookie-handler) "/cookiecheck") context)) (defn basic-test [http-method java-method clj-fn] (testing (format "sync client: HTTP method: '%s'" http-method) (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:port 10000}} (testing "java sync client" (let [request-options (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) response (java-method request-options)] (is (= 200 (.getStatus response))) (is (= "OK" (.getReasonPhrase response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "clojure sync client" (let [response (clj-fn "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "OK" (:reason-phrase response))) (is (= "Hello, World!" (slurp (:body response)))))))))) (deftest sync-client-head-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:port 10000}} (testing "java sync client" (let [request-options (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) response (Sync/head request-options)] (is (= 200 (.getStatus response))) (is (= nil (.getBody response))))) (testing "clojure sync client" (let [response (sync/head "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= nil (:body response))))) (testing "not found" (let [response (sync/head "http://localhost:10000/missing")] (is (= 404 (:status response))) (is (= "Not Found" (:reason-phrase response)))))))) (deftest sync-client-get-test (basic-test "GET" #(Sync/get %) sync/get)) (deftest sync-client-post-test (basic-test "POST" #(Sync/post %) sync/post)) (deftest sync-client-put-test (basic-test "PUT" #(Sync/put %) sync/put)) (deftest sync-client-delete-test (basic-test "DELETE" #(Sync/delete %) sync/delete)) (deftest sync-client-trace-test (basic-test "TRACE" #(Sync/trace %) sync/trace)) (deftest sync-client-options-test (basic-test "OPTIONS" #(Sync/options %) sync/options)) (deftest sync-client-patch-test (basic-test "PATCH" #(Sync/patch %) sync/patch)) (deftest sync-client-persistent-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:port 10000}} (testing "persistent java client" (let [request-options (RequestOptions. "http://localhost:10000/hello/") client-options (ClientOptions.) client (Sync/createClient client-options)] (testing "HEAD request with persistent sync client" (let [response (.head client request-options)] (is (= 200 (.getStatus response))) (is (= nil (.getBody response))))) (testing "GET request with persistent sync client" (let [response (.get client request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "POST request with persistent sync client" (let [response (.post client request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "PUT request with persistent sync client" (let [response (.put client request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "DELETE request with persistent sync client" (let [response (.delete client request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "TRACE request with persistent sync client" (let [response (.trace client request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "OPTIONS request with persistent sync client" (let [response (.options client request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "PATCH request with persistent sync client" (let [response (.patch client request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "client closes properly" (.close client) (is (thrown? IllegalStateException (.get client request-options)))))) (testing "persistent clojure client" (let [client (sync/create-client {})] (testing "HEAD request with persistent sync client" (let [response (common/head client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= nil (:body response))))) (testing "GET request with persistent sync client" (let [response (common/get client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "POST request with persistent sync client" (let [response (common/post client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "PUT request with persistent sync client" (let [response (common/put client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "DELETE request with persistent sync client" (let [response (common/delete client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "TRACE request with persistent sync client" (let [response (common/trace client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "OPTIONS request with persistent sync client" (let [response (common/options client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "PATCH request with persistent sync client" (let [response (common/patch client "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "GET request via request function with persistent sync client" (let [response (common/make-request client "http://localhost:10000/hello/" :get)] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "Bad verb request via request function with persistent sync client" (is (thrown? IllegalArgumentException (common/make-request client "http://localhost:10000/hello/" :bad)))) (testing "client closes properly" (common/close client) (is (thrown? IllegalStateException (common/get client "http://localhost:10000/hello"))))))))) (deftest sync-client-as-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:port 10000}} (testing "java sync client: :as unspecified" (let [request-options (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (instance? InputStream (.getBody response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "java sync client: :as :stream" (let [request-options (.. (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) (setAs ResponseBodyType/STREAM)) response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (instance? InputStream (.getBody response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "java sync client: :as :text" (let [request-options (.. (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) (setAs ResponseBodyType/TEXT)) response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (string? (.getBody response))) (is (= "Hello, World!" (.getBody response))))) (testing "clojure sync client: :as unspecified" (let [response (sync/get "http://localhost:10000/hello/")] (is (= 200 (:status response))) (is (instance? InputStream (:body response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "clojure sync client: :as :stream" (let [response (sync/get "http://localhost:10000/hello/" {:as :stream})] (is (= 200 (:status response))) (is (instance? InputStream (:body response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "clojure sync client: :as :text" (let [response (sync/get "http://localhost:10000/hello/" {:as :text})] (is (= 200 (:status response))) (is (string? (:body response))) (is (= "Hello, World!" (:body response)))))))) (deftest request-with-client-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:port 10000}} (let [client (HttpAsyncClients/createDefault) opts {:method :get :url "http://localhost:10000/hello/"}] (.start client) (testing "GET request works with request-with-client" (let [response (sync/request-with-client opts client)] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (testing "Client persists when passed to request-with-client" (let [response (sync/request-with-client opts client)] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))) (.close client))))) (deftest java-api-cookie-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-cookie-service] {:webserver {:port 10000}} (let [client (Sync/createClient (ClientOptions.))] (testing "Set a cookie using Java API" (let [response (.get client (RequestOptions. "http://localhost:10000/cookietest"))] (is (= 200 (.getStatus response))))) (testing "Check if cookie still exists" (let [response (.get client (RequestOptions. "http://localhost:10000/cookiecheck"))] (is (= 200 (.getStatus response))))))))) (deftest clj-api-cookie-test (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-cookie-service] {:webserver {:port 10000}} (let [client (sync/create-client {})] (testing "Set a cookie using Clojure API" (let [response (common/get client "http://localhost:10000/cookietest")] (is (= 200 (:status response))))) (testing "Check if cookie still exists" (let [response (common/get client "http://localhost:10000/cookiecheck")] (is (= 200 (:status response))))))))) (defn header-app [req] (let [val (get-in req [:headers "fooheader"])] {:status 200 :headers {"myrespheader" val} :body val})) (tk/defservice test-header-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler header-app "/hello") context)) (deftest sync-client-request-headers-test (testlogging/with-test-logging (testutils/with-app-with-config header-app [jetty9/jetty9-service test-header-web-service] {:webserver {:port 10000}} (testing "java sync client" (let [request-options (-> (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) (.setHeaders {"fooheader" "foo"})) response (Sync/post request-options)] (is (= 200 (.getStatus response))) (is (= "foo" (slurp (.getBody response)))) (is (= "foo" (-> (.getHeaders response) (.get "myrespheader")))))) (testing "clojure sync client" (let [response (sync/post "http://localhost:10000/hello/" {:headers {"fooheader" "foo"}})] (is (= 200 (:status response))) (is (= "foo" (slurp (:body response)))) (is (= "foo" (get-in response [:headers "myrespheader"])))))))) (defn req-body-app [req] {:status 200 :headers (if-let [content-type (:content-type req)] {"Content-Type" (:content-type req)}) :body (slurp (:body req))}) (tk/defservice test-body-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler req-body-app "/hello") context)) (defn- validate-java-request [body-to-send headers-to-send expected-content-type expected-response-body] (let [request-options (-> (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) (.setBody body-to-send) (.setHeaders headers-to-send)) response (Sync/post request-options)] (is (= 200 (.getStatus response))) (is (= (-> (.getHeaders response) (.get "content-type")) expected-content-type)) (is (= expected-response-body (slurp (.getBody response)))))) (defn- validate-clj-request [body-to-send headers-to-send expected-content-type expected-response-body] (let [response (sync/post "http://localhost:10000/hello/" {:body body-to-send :headers headers-to-send})] (is (= 200 (:status response))) (is (= (get-in response [:headers "content-type"]) expected-content-type)) (is (= expected-response-body (slurp (:body response)))))) (deftest sync-client-request-body-test (testlogging/with-test-logging (testutils/with-app-with-config req-body-app [jetty9/jetty9-service test-body-web-service] {:webserver {:port 10000}} (testing "java sync client: string body for post request with explicit content type and UTF-8 encoding uses UTF-8 encoding" (validate-java-request "foo�" {"Content-Type" "text/plain; charset=utf-8"} "text/plain;charset=utf-8" "foo�")) (testing "java sync client: string body for post request with explicit content type and ISO-8859-1 encoding uses ISO-8859-1 encoding" (validate-java-request "foo�" {"Content-Type" "text/plain; charset=iso-8859-1"} "text/plain;charset=iso-8859-1" "foo?")) (testing "java sync client: string body for post request with explicit content type but without explicit encoding uses UTF-8 encoding" (validate-java-request "foo�" {"Content-Type" "text/plain"} "text/plain;charset=utf-8" "foo�")) (testing "java sync client: string body for post request without explicit content or encoding uses ISO-8859-1 encoding" (validate-java-request "foo�" nil "text/plain;charset=iso-8859-1" "foo?")) (testing "java sync client: input stream body for post request" (let [request-options (-> (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) (.setBody (ByteArrayInputStream. (.getBytes "foo�" "UTF-8"))) (.setHeaders {"Content-Type" "text/plain; charset=UTF-8"})) response (Sync/post request-options)] (is (= 200 (.getStatus response))) (is (= "foo�" (slurp (.getBody response)))))) (testing "clojure sync client: string body for post request with explicit content type and UTF-8 encoding uses UTF-8 encoding" (validate-clj-request "foo�" {"content-type" "text/plain; charset=utf-8"} "text/plain;charset=utf-8" "foo�")) (testing "clojure sync client: string body for post request with explicit content type and ISO-8859 encoding uses ISO-8859-1 encoding" (validate-clj-request "foo�" {"content-type" "text/plain; charset=iso-8859-1"} "text/plain;charset=iso-8859-1" "foo?")) (testing "clojure sync client: string body for post request with explicit content type but without explicit encoding uses UTF-8 encoding" (validate-clj-request "foo�" {"content-type" "text/plain"} "text/plain;charset=utf-8" "foo�")) (testing "clojure sync client: string body for post request without explicit content type or encoding uses ISO-8859-1 encoding" (validate-clj-request "foo�" {} "text/plain;charset=iso-8859-1" "foo?")) (testing "clojure sync client: input stream body for post request" (let [response (sync/post "http://localhost:10000/hello/" {:body (io/input-stream (.getBytes "foo�" "UTF-8")) :headers {"content-type" "text/plain; charset=UTF-8"}})] (is (= 200 (:status response))) (is (= "foo�" (slurp (:body response))))))))) (def compressible-body (apply str (repeat 1000 "f"))) (defn compression-app [req] {:status 200 :headers {"orig-accept-encoding" (get-in req [:headers "accept-encoding"]) "content-type" "text/plain" "charset" "UTF-8"} :body compressible-body}) (tk/defservice test-compression-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler compression-app "/hello") context)) (defn test-compression [desc opts accept-encoding content-encoding content-should-match?] (testlogging/with-test-logging (testutils/with-app-with-config req-body-app [jetty9/jetty9-service test-compression-web-service] {:webserver {:port 10000}} (testing (str "java sync client: compression headers / response: " desc) (let [request-opts (cond-> (SimpleRequestOptions. (URI. "http://localhost:10000/hello/")) (contains? opts :decompress-body) (.setDecompressBody (:decompress-body opts)) (contains? opts :headers) (.setHeaders (:headers opts))) response (Sync/get request-opts)] (is (= 200 (.getStatus response))) (is (= accept-encoding (.. response getHeaders (get "orig-accept-encoding")))) (is (= content-encoding (.. response getOrigContentEncoding))) (if content-should-match? (is (= compressible-body (slurp (.getBody response)))) (is (not= compressible-body (slurp (.getBody response))))))) (testing (str "clojure sync client: compression headers / response: " desc) (let [response (sync/post "http://localhost:10000/hello/" opts)] (is (= 200 (:status response))) (is (= accept-encoding (get-in response [:headers "orig-accept-encoding"]))) (is (= content-encoding (:orig-content-encoding response))) (if content-should-match? (is (= compressible-body (slurp (:body response)))) (is (not= compressible-body (slurp (:body response)))))))))) (deftest sync-client-compression-test (test-compression "default" {} "gzip, deflate" "gzip" true)) (deftest sync-client-compression-gzip-test (test-compression "explicit gzip" {:headers {"accept-encoding" "gzip"}} "gzip" "gzip" true)) (deftest sync-client-compression-disabled-test (test-compression "explicit disable" {:decompress-body false} nil nil true)) (deftest sync-client-decompression-disabled-test (test-compression "explicit disable" {:headers {"accept-encoding" "gzip"} :decompress-body false} "gzip" "gzip" false)) (deftest query-params-test-sync (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-params-web-service] {:webserver {:port 8080}} (testing "URL Query Parameters work with the Java client" (let [request-options (SimpleRequestOptions. (URI. "http://localhost:8080/params?foo=bar&baz=lux"))] (let [response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (= queryparams (read-string (slurp (.getBody response)))))))) (testing "URL Query Parameters work with the clojure client" (let [opts {:method :get :url "http://localhost:8080/params/" :query-params queryparams :as :text} response (sync/get "http://localhost:8080/params" opts)] (is (= 200 (:status response))) (is (= queryparams (read-string (:body response))))))))) (deftest redirect-test-sync (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service redirect-web-service] {:webserver {:port 8080}} (testing (str "redirects on POST not followed by Java client " "when forceRedirects option not set to true") (let [request-options (SimpleRequestOptions. (URI. "http://localhost:8080/hello")) response (Sync/post request-options)] (is (= 302 (.getStatus response))))) (testing "redirects on POST followed by Java client when option is set" (let [request-options (.. (SimpleRequestOptions. (URI. "http://localhost:8080/hello")) (setForceRedirects true)) response (Sync/post request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "redirects not followed by Java client when :follow-redirects is false" (let [request-options (.. (SimpleRequestOptions. (URI. "http://localhost:8080/hello")) (setFollowRedirects false)) response (Sync/get request-options)] (is (= 302 (.getStatus response))))) (testing ":follow-redirects overrides :force-redirects for Java client" (let [request-options (.. (SimpleRequestOptions. (URI. "http://localhost:8080/hello")) (setFollowRedirects false) (setForceRedirects true)) response (Sync/get request-options)] (is (= 302 (.getStatus response))))) (testing (str "redirects on POST not followed by clojure client " "when :force-redirects is not set to true") (let [opts {:method :post :url "http://localhost:8080/hello" :as :text :force-redirects false} response (sync/post "http://localhost:8080/hello" opts)] (is (= 302 (:status response))))) (testing "redirects on POST followed by clojure client when option is set" (let [opts {:method :post :url "http://localhost:8080/hello" :as :text :force-redirects true} response (sync/post "http://localhost:8080/hello" opts)] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))))) (testing (str "redirects not followed by clojure client when :follow-redirects " "is set to false") (let [response (sync/get "http://localhost:8080/hello" {:as :text :follow-redirects false})] (is (= 302 (:status response))))) (testing ":follow-redirects overrides :force-redirects with clojure client" (let [response (sync/get "http://localhost:8080/hello" {:as :text :follow-redirects false :force-redirects true})] (is (= 302 (:status response))))) (testing (str "redirects on POST followed by persistent clojure client " "when option is set") (let [client (sync/create-client {:force-redirects true}) response (common/post client "http://localhost:8080/hello" {:as :text})] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))) (common/close client))) (testing (str "persistent clojure client does not follow redirects when " ":follow-redirects is set to false") (let [client (sync/create-client {:follow-redirects false}) response (common/get client "http://localhost:8080/hello" {:as :text})] (is (= 302 (:status response))) (common/close client))) (testing ":follow-redirects overrides :force-redirects with persistent clj client" (let [client (sync/create-client {:follow-redirects false :force-redirects true}) response (common/get client "http://localhost:8080/hello" {:as :text})] (is (= 302 (:status response))) (common/close client)))))) (defmacro wrapped-connect-exception-thrown? [& body] `(try (testlogging/with-test-logging ~@body) (throw (IllegalStateException. "Expected HttpClientException but none thrown!")) (catch HttpClientException e# (if-let [cause# (.getCause e#)] (or (instance? SocketTimeoutException cause#) (instance? ConnectException cause#) (throw (IllegalStateException. (str "Expected SocketTimeoutException or ConnectException " "cause but found: " cause#)))) (throw (IllegalStateException. (str "Expected SocketTimeoutException or ConnectException but " "no cause found. Message:" (.getMessage e#)))))))) (defmacro wrapped-timeout-exception-thrown? [& body] `(try (testlogging/with-test-logging ~@body) (throw (IllegalStateException. "Expected HttpClientException but none thrown!")) (catch HttpClientException e# (if-let [cause# (.getCause e#)] (or (instance? SocketTimeoutException cause#) (throw (IllegalStateException. (str "Expected SocketTimeoutException cause but found: " cause#)))) (throw (IllegalStateException. (str "Expected SocketTimeoutException but no cause found. " "Message: " (.getMessage e#)))))))) (deftest short-connect-timeout-nonpersistent-java-test-sync (testing (str "connection times out properly for non-persistent java sync " "request with short timeout") (let [request-options (-> "http://127.0.0.255:65535" (SimpleRequestOptions.) (.setConnectTimeoutMilliseconds 250)) time-before-connect (System/currentTimeMillis)] (is (wrapped-connect-exception-thrown? (Sync/get request-options)) "Unexpected result for connection attempt") (is (elapsed-within-range? time-before-connect 2000) "Connection attempt took significantly longer than timeout")))) (deftest short-connect-timeout-persistent-java-test-sync (testing (str "connection times out properly for java persistent client sync " "request with short timeout") (with-open [client (-> (ClientOptions.) (.setConnectTimeoutMilliseconds 250) (Sync/createClient))] (let [request-options (RequestOptions. "http://127.0.0.255:65535") time-before-connect (System/currentTimeMillis)] (is (wrapped-connect-exception-thrown? (.get client request-options)) "Unexpected result for connection attempt") (is (elapsed-within-range? time-before-connect 2000) "Connection attempt took significantly longer than timeout"))))) (deftest short-connect-timeout-nonpersistent-clojure-test-sync (testing (str "connection times out properly for non-persistent clojure sync " "request with short timeout") (let [time-before-connect (System/currentTimeMillis)] (is (connect-exception-thrown? (sync/get "http://127.0.0.255:65535" {:connect-timeout-milliseconds 250})) "Unexpected result for connection attempt") (is (elapsed-within-range? time-before-connect 2000) "Connection attempt took significantly longer than timeout")))) (deftest short-connect-timeout-persistent-clojure-test-sync (testing (str "connection times out properly for clojure persistent client " "sync request with short timeout") (with-open [client (sync/create-client {:connect-timeout-milliseconds 250})] (let [time-before-connect (System/currentTimeMillis)] (is (connect-exception-thrown? (common/get client "http://127.0.0.255:65535")) "Unexpected result for connection attempt") (is (elapsed-within-range? time-before-connect 2000) "Connection attempt took significantly longer than timeout"))))) (deftest longer-connect-timeout-test-sync (testing "connection succeeds for sync request with longer connect timeout" (testlogging/with-test-logging (testwebserver/with-test-webserver app port (let [url (str "http://localhost:" port "/hello")] (testing "java non-persistent sync client" (let [request-options (.. (SimpleRequestOptions. url) (setConnectTimeoutMilliseconds 2000)) response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "java persistent sync client" (with-open [client (-> (ClientOptions.) (.setConnectTimeoutMilliseconds 2000) (Sync/createClient))] (let [response (.get client (RequestOptions. url))] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response))))))) (testing "clojure non-persistent sync client" (let [response (sync/get url {:as :text :connect-timeout-milliseconds 2000})] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))))) (testing "clojure persistent sync client" (with-open [client (sync/create-client {:connect-timeout-milliseconds 2000})] (let [response (common/get client url {:as :text})] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))))))))))) (deftest short-socket-timeout-nonpersistent-java-test-sync (testing (str "socket read times out properly for non-persistent java sync " "request with short timeout") (with-open [server (ServerSocket. 0)] (let [request-options (-> "http://127.0.0.1:" (str (.getLocalPort server)) (SimpleRequestOptions.) (.setSocketTimeoutMilliseconds 250)) time-before-connect (System/currentTimeMillis)] (is (wrapped-timeout-exception-thrown? (Sync/get request-options)) "Unexpected result for get attempt") (is (elapsed-within-range? time-before-connect 2000) "Get attempt took significantly longer than timeout"))))) (deftest short-socket-timeout-persistent-java-test-sync (testing (str "socket read times out properly for persistent java sync " "request with short timeout") (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 250) (Sync/createClient)) server (ServerSocket. 0)] (let [request-options (-> "http://127.0.0.1:" (str (.getLocalPort server)) (RequestOptions.)) time-before-connect (System/currentTimeMillis)] (is (wrapped-timeout-exception-thrown? (.get client request-options)) "Unexpected result for get attempt") (is (elapsed-within-range? time-before-connect 2000) "Get attempt took significantly longer than timeout"))))) (deftest short-socket-timeout-nonpersistent-clojure-test-sync (testing (str "socket read times out properly for non-persistent clojure " "sync request with short timeout") (with-open [server (ServerSocket. 0)] (let [url (str "http://127.0.0.1:" (.getLocalPort server)) time-before-connect (System/currentTimeMillis)] (is (thrown? SocketTimeoutException (sync/get url {:socket-timeout-milliseconds 250})) "Unexpected result for get attempt") (is (elapsed-within-range? time-before-connect 2000) "Get attempt took significantly longer than timeout"))))) (deftest short-socket-timeout-persistent-clojure-test-sync (testing (str "socket read times out properly for clojure persistent client " "sync request with short timeout") (with-open [client (sync/create-client {:socket-timeout-milliseconds 250}) server (ServerSocket. 0)] (let [url (str "http://127.0.0.1:" (.getLocalPort server)) time-before-connect (System/currentTimeMillis)] (is (thrown? SocketTimeoutException (common/get client url)) "Unexpected result for get attempt") (is (elapsed-within-range? time-before-connect 2000) "Get attempt took significantly longer than timeout"))))) (deftest longer-socket-timeout-test-sync (testing "get succeeds for sync request with longer socket timeout" (testlogging/with-test-logging (testwebserver/with-test-webserver app port (let [url (str "http://localhost:" port "/hello")] (testing "java non-persistent sync client" (let [request-options (.. (SimpleRequestOptions. url) (setSocketTimeoutMilliseconds 2000)) response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "java persistent sync client" (with-open [client (-> (ClientOptions.) (.setSocketTimeoutMilliseconds 2000) (Sync/createClient))] (let [response (.get client (RequestOptions. url))] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response))))))) (testing "clojure non-persistent sync client" (let [response (sync/get url {:as :text :socket-timeout-milliseconds 2000})] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))))) (testing "clojure persistent sync client" (with-open [client (sync/create-client {:socket-timeout-milliseconds 2000})] (let [response (common/get client url {:as :text})] (is (= 200 (:status response))) (is (= "Hello, World!" (:body response))))))))))) clj-http-client-1.2.0/test/puppetlabs/http/client/sync_ssl_test.clj000066400000000000000000000217171373223535200255110ustar00rootroot00000000000000(ns puppetlabs.http.client.sync-ssl-test (:import (com.puppetlabs.http.client Sync HttpClientException SimpleRequestOptions) (javax.net.ssl SSLHandshakeException SSLException) (java.net URI ConnectException) (org.apache.http ConnectionClosedException) (com.puppetlabs.ssl_utils SSLUtils)) (:require [clojure.test :refer :all] [puppetlabs.trapperkeeper.core :as tk] [puppetlabs.trapperkeeper.testutils.bootstrap :as testutils] [puppetlabs.trapperkeeper.testutils.logging :as testlogging] [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9] [puppetlabs.http.client.sync :as sync] [schema.test :as schema-test] [clojure.tools.logging :as log])) (use-fixtures :once schema-test/validate-schemas) (defn app [req] {:status 200 :body "Hello, World!"}) (tk/defservice test-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler app "/hello") context)) (deftest sync-client-test-from-pems (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:ssl-host "0.0.0.0" :ssl-port 10080 :ssl-ca-cert "./dev-resources/ssl/ca.pem" :ssl-cert "./dev-resources/ssl/cert.pem" :ssl-key "./dev-resources/ssl/key.pem"}} (testing "java sync client" (let [request-options (.. (SimpleRequestOptions. (URI. "https://localhost:10080/hello/")) (setSslCert "./dev-resources/ssl/cert.pem") (setSslKey "./dev-resources/ssl/key.pem") (setSslCaCert "./dev-resources/ssl/ca.pem")) response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "clojure sync client" (let [response (sync/get "https://localhost:10080/hello/" {:ssl-cert "./dev-resources/ssl/cert.pem" :ssl-key "./dev-resources/ssl/key.pem" :ssl-ca-cert "./dev-resources/ssl/ca.pem"})] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response))))))))) (deftest sync-client-test-from-ca-cert (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:ssl-host "0.0.0.0" :ssl-port 10080 :ssl-ca-cert "./dev-resources/ssl/ca.pem" :ssl-cert "./dev-resources/ssl/cert.pem" :ssl-key "./dev-resources/ssl/key.pem" :client-auth "want"}} (testing "java sync client" (let [request-options (.. (SimpleRequestOptions. (URI. "https://localhost:10080/hello/")) (setSslCaCert "./dev-resources/ssl/ca.pem")) response (Sync/get request-options)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "clojure sync client" (let [response (sync/get "https://localhost:10080/hello/" {:ssl-ca-cert "./dev-resources/ssl/ca.pem"})] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response))))))))) (deftest sync-client-test-with-invalid-ca-cert (testlogging/with-test-logging (testutils/with-app-with-config app [jetty9/jetty9-service test-web-service] {:webserver {:ssl-host "0.0.0.0" :ssl-port 10081 :ssl-ca-cert "./dev-resources/ssl/ca.pem" :ssl-cert "./dev-resources/ssl/cert.pem" :ssl-key "./dev-resources/ssl/key.pem" :client-auth "want"}} (testing "java sync client" (let [request-options (.. (SimpleRequestOptions. (URI. "https://localhost:10081/hello/")) (setSslCaCert "./dev-resources/ssl/alternate-ca.pem"))] (try (Sync/get request-options) ; fail if we don't get an exception (is (not true) "expected HttpClientException") (catch HttpClientException e (if (SSLUtils/isFIPS) ;; in FIPS, the BC provider throws a different exception here, specifically: ;; javax.net.ssl.SSLException: org.bouncycastle.tls.TlsFatalAlert: certificate_unknown(46) (is (instance? SSLException (.getCause e))) (is (instance? SSLHandshakeException (.getCause e)))))))) (testing "clojure sync client" (if (SSLUtils/isFIPS) ;; in FIPS, the BC provider throws a different exception here, specifically: ;; javax.net.ssl.SSLException: org.bouncycastle.tls.TlsFatalAlert: certificate_unknown(46) (is (thrown? SSLException (sync/get "https://localhost:10081/hello/" {:ssl-ca-cert "./dev-resources/ssl/alternate-ca.pem"}))) (is (thrown? SSLHandshakeException (sync/get "https://localhost:10081/hello/" {:ssl-ca-cert "./dev-resources/ssl/alternate-ca.pem"})))))))) (defmacro with-server-with-protocols [server-protocols server-cipher-suites & body] `(testlogging/with-test-logging (testutils/with-app-with-config app# [jetty9/jetty9-service test-web-service] {:webserver (merge {:ssl-host "0.0.0.0" :ssl-port 10080 :ssl-ca-cert "./dev-resources/ssl/ca.pem" :ssl-cert "./dev-resources/ssl/cert.pem" :ssl-key "./dev-resources/ssl/key.pem" :ssl-protocols ~server-protocols} (if ~server-cipher-suites {:cipher-suites ~server-cipher-suites}))} ~@body))) (defmacro java-unsupported-protocol-exception? [& body] `(try ~@body (catch HttpClientException e# (let [cause# (.getCause e#) message# (.getMessage cause#)] (or (and (instance? SSLHandshakeException cause#) (or ;; java 11 (re-find #"protocol_version" message#) ;; java 8 (re-find #"not supported by the client" message#))) (and (instance? SSLException cause#) (or (re-find #"handshake_failure" message#) (re-find #"internal_error" message#))) (instance? ConnectionClosedException cause#)))))) (defn java-https-get-with-protocols [client-protocols client-cipher-suites] (let [request-options (.. (SimpleRequestOptions. (URI. "https://localhost:10080/hello/")) (setSslCert "./dev-resources/ssl/cert.pem") (setSslKey "./dev-resources/ssl/key.pem") (setSslCaCert "./dev-resources/ssl/ca.pem"))] (if client-protocols (.setSslProtocols request-options (into-array String client-protocols))) (if client-cipher-suites (.setSslCipherSuites request-options (into-array String client-cipher-suites))) (Sync/get request-options))) (defn clj-https-get-with-protocols [client-protocols client-cipher-suites] (let [ssl-opts (merge {:ssl-cert "./dev-resources/ssl/cert.pem" :ssl-key "./dev-resources/ssl/key.pem" :ssl-ca-cert "./dev-resources/ssl/ca.pem"} (if client-protocols {:ssl-protocols client-protocols}) (if client-cipher-suites {:cipher-suites client-cipher-suites}))] (sync/get "https://localhost:10080/hello/" ssl-opts))) (deftest sync-client-test-ssl-protocols (testing "should be able to connect to a TLSv1.2 server by default" (with-server-with-protocols ["TLSv1.2"] nil (testing "java sync client" (let [response (java-https-get-with-protocols nil nil)] (is (= 200 (.getStatus response))) (is (= "Hello, World!" (slurp (.getBody response)))))) (testing "clojure sync client" (let [response (clj-https-get-with-protocols nil nil)] (is (= 200 (:status response))) (is (= "Hello, World!" (slurp (:body response)))))))) (testing "should not connect to a server when protocols don't overlap" (with-server-with-protocols ["TLSv1.1"] ["TLS_RSA_WITH_AES_128_CBC_SHA"] (testing "java sync client" (is (java-unsupported-protocol-exception? (java-https-get-with-protocols ["TLSv1.2"] nil)))) (testing "clojure sync client" (is (thrown? SSLException (clj-https-get-with-protocols ["TLSv1.2"] nil))))))) clj-http-client-1.2.0/test/puppetlabs/http/client/test_common.clj000066400000000000000000000032331373223535200251350ustar00rootroot00000000000000(ns puppetlabs.http.client.test-common (:require [ring.middleware.params :as ring-params] [puppetlabs.trapperkeeper.core :as tk] [puppetlabs.trapperkeeper.testutils.logging :as testlogging]) (:import (java.net ConnectException SocketTimeoutException))) (defn query-params-test [req] {:status 200 :body (str (:query-params req))}) (def app-wrapped (ring-params/wrap-params query-params-test)) (tk/defservice test-params-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler app-wrapped "/params") context)) (def queryparams {"foo" "bar" "baz" "lux"}) (def query-options {:method :get :url "http://localhost:8080/params/" :query-params queryparams :as :text}) (defn redirect-test-handler [req] (condp = (:uri req) "/hello/world" {:status 200 :body "Hello, World!"} "/hello" {:status 302 :headers {"Location" "/hello/world"} :body ""} {:status 404 :body "D'oh"})) (tk/defservice redirect-web-service [[:WebserverService add-ring-handler]] (init [this context] (add-ring-handler redirect-test-handler "/hello") context)) (defmacro connect-exception-thrown? [& body] `(try (testlogging/with-test-logging ~@body) (catch SocketTimeoutException _# true) (catch ConnectException _# true))) (defn elapsed-within-range? [start-time-milliseconds duration-milliseconds] (<= (System/currentTimeMillis) (+ start-time-milliseconds duration-milliseconds)))