pax_global_header00006660000000000000000000000064137416256760014533gustar00rootroot0000000000000052 comment=9ea674055353a5ffa29ad957ce80f0c04cefb48e ring-middleware-1.3.0/000077500000000000000000000000001374162567600146065ustar00rootroot00000000000000ring-middleware-1.3.0/.gitignore000066400000000000000000000003271374162567600166000ustar00rootroot00000000000000.nrepl-port pom.xml pom.xml.asc *jar /lib/ /classes/ /target/ /checkouts/ .lein-deps-sum .lein-repl-history .lein-plugins/ .lein-failures /resources/locales.clj /dev-resources/i18n/bin /resources/**/Messages*.class ring-middleware-1.3.0/.travis.yml000066400000000000000000000004041374162567600167150ustar00rootroot00000000000000language: clojure lein: 2.7.1 jdk: - openjdk8 script: ./ext/travisci/test.sh notifications: email: false # workaround for buffer overflow issue, ref https://github.com/travis-ci/travis-ci/issues/522e addons: hosts: - myshorthost hostname: myshorthost ring-middleware-1.3.0/CHANGELOG.md000066400000000000000000000050001374162567600164120ustar00rootroot00000000000000# 1.3.0 * This version updates the `wrap-add-cache-headers` middleware so that it adds a `cache-control` header with the "no-store" directive instead of the "private, max-age=0, no-cache" directives. # 1.2.0 * This version adds `wrap-params` middleware with custom implementation of the `params-request` function. This was copied from the puppetserver repo to this more-central location. It is documented in the [README](./README.md). # 1.1.0 * This version adds two new middleware used in other puppetlabs projects, documented in the [README](./README.md): * `wrap-add-x-content-nosniff` * `wrap-add-csp` # 1.0.1 * This is a bug fix release that ensure stacktraces are correctly printed to the log when handling otherwise uncaught exceptions. # 1.0.0 #### Breaking Changes * Moves from `{:type ... :message ...}` to `{:kind ... :msg ...}` for exceptions and error responses. * Moves schemas and helpers previously defined in `core` namespace into new `utils` namespace. # 0.3.1 * This is a bug-fix release for a regression in wrap-proxy. * All middleware now have the `:always-validate` metadata set for schema validation. # 0.3.0 * This version adds many middleware that are used in other puppetlabs projects. These middleware are mostly for logging and error handling, and they are all documented in the [README](./README.md): * `wrap-request-logging` * `wrap-response-logging` * `wrap-service-unavailable` * `wrap-bad-request` * `wrap-data-errors` * `wrap-schema-errors` * `wrap-uncaught-errors` * Additionally, this version fixes [an issue](https://tickets.puppetlabs.com/browse/TK-228) with the behavior of `wrap-proxy` and its handling of redirects. # 0.2.1 * Add wrap-with-certificate-cn middleware that adds a `:ssl-client-cn` key to the request map if a `:ssl-client-cert` is present. * Add wrap-with-x-frame-options-deny middleware that adds `X-Frame-Options: DENY` # 0.2.0 * Modify behavior of regex support in the wrap-proxy function. Now, when a regex is given for the `proxied-path` argument, the entirety of the request uri's path will be appended onto the path of `remote-uri-base`. * Add a new utility middleware, `wrap-add-cache-headers`, that adds `cache-control` headers to `GET` and `PUT` requests. # 0.1.3 * Log proxied requests * Allow `proxied-path` argument in `wrap-proxy` function to be a regular expression * Bump http-client to v0.2.8 # 0.1.2 * Add support for redirect following on proxy requests * Fix issue where Gzipped proxy responses were being truncated ring-middleware-1.3.0/CODEOWNERS000066400000000000000000000002621374162567600162010ustar00rootroot00000000000000# This will cause the puppetserver-maintainers group to be assigned # review of any opened PRs against the branches containing this file. * @puppetlabs/puppetserver-maintainers ring-middleware-1.3.0/CONTRIBUTING.md000066400000000000000000000010171374162567600170360ustar00rootroot00000000000000# 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) ring-middleware-1.3.0/LICENSE000066400000000000000000000260741374162567600156240ustar00rootroot00000000000000Apache 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. ring-middleware-1.3.0/Makefile000066400000000000000000000000441374162567600162440ustar00rootroot00000000000000include dev-resources/Makefile.i18n ring-middleware-1.3.0/README.md000066400000000000000000000255161374162567600160760ustar00rootroot00000000000000# ring-middleware [![Build Status](https://travis-ci.org/puppetlabs/ring-middleware.png?branch=master)](https://travis-ci.org/puppetlabs/ring-middleware) This project was originally adapted from tailrecursion's [ring-proxy](https://github.com/tailrecursion/ring-proxy) middleware, and is meant for use with the [Trapperkeeper Jetty9 Webservice](https://github.com/puppetlabs/trapperkeeper-webserver-jetty9). It also contains common ring middleware between Puppet projects and helpers to be used with the middleware. ## Usage To use `ring-middleware`, add this project as a dependency in your leiningen project file: [![Clojars Project](http://clojars.org/puppetlabs/ring-middleware/latest-version.svg)](https://clojars.org/puppetlabs/ring-middleware) ## Schemas * `ResponseType` -- one of the two supported response types (`:json`, `:plain`) returned by many middleware. * `RingRequest` -- a map containing at least a `:uri`, optionally a valid certificate, and any number of keyword-Any pairs. * `RingResponse` -- a map with at least `:status`, `:headers`, and `:body` keys. ## Non-Middleware Helpers ### json-response ```clj (json-response status body) ``` Creates a basic ring response with `:status` of `status` and a `:body` of `body` serialized to json. ### plain-response ```clj (plain-response status body) ``` Creates a basic ring response with `:status` of `status` and a `:body` of `body` set to UTF-8 plain text. ### throw-bad-request! ```clj (throw-bad-request! "Error Message") ``` Throws a :bad-request type slingshot error with the supplied message. See `wrap-bad-request` for middleware designed to compliment this function, also `bad-request?` for a function to help implement your own error handling. ### throw-service-unavailable! ```clj (throw-service-unavailable! "Error Message") ``` Throws a :service-unavailable type slingshot error with the supplied message. See `wrap-service-unavailable` for middleware designed to compliment this function, also `service-unavailable?` for a function to help implement your own error handling. ### throw-data-invalid! ```clj (throw-data-invalid! "Error Message") ``` Throws a :data-invalid type slingshot error with the supplied message. See `wrap-data-errors` for middleware designed to compliment this function, also `data-invalid?` for a function to help implement your own error handling. ### bad-request? ```clj (try+ (handler request) (catch bad-request? e (...handle a bad request...))) ``` Determines if the supplied slingshot error map is for a bad request. ### service-unavailable? ```clj (try+ (handler request) (catch service-unavailable? e (...handle service unavailability...))) ``` Determines if the supplied slingshot error map is for the service being unavailable. ### data-invalid? ```clj (try+ (handler request) (catch data-invalid? e (...handle invalid data...))) ``` Determines if the supplied slingshot error map is for invalid data. ### schema-error? ```clj (try+ (handler request) (catch schema-error? e (...handle schema error...))) ``` Determines if the supplied slingshot error map is for a schema error. ## Middleware ### wrap-request-logging ```clj (wrap-request-logging handler) ``` Logs the `:request-method` and `:uri` at debug level, the full request at trace. At the trace level, attempts to remove sensitive auth information and replace client certificate with the client's common name. ### wrap-response-logging ```clj (wrap-response-logging handler) ``` Logs the response at the trace log level. ### wrap-proxy ```clj (wrap-proxy handler proxied-path remote-uri-base & [http-opts]) ``` This function returns a ring handler that, when given a URL with a certain prefix, proxies the request to a remote URL specified by the `remote-uri-base` argument. The arguments are as follows: * `handler`: A ring-handler that will be used if the provided url does not begin with the proxied-path prefix * `proxied-path`: The URL prefix of all requests that are to be proxied. This can be either a string or a regular expression pattern. Note that, when this is a regular expression, the entire request URI will be appended to `remote-uri-base` when the URI is being rewritten, whereas if this argument is a string, the `proxied-path` will not be included. * `remote-uri-base`: The base URL that you want to proxy requests with the `proxied-path` prefix to * `http-opts`: An optional list of options for an http client. This is used by the handler returned by `wrap-proxy` when it makes a proxied request to a remote URI. For a list of available options, please see the options defined for [clj-http-client](https://github.com/puppetlabs/clj-http-client). For example, the following: ```clj (wrap-proxy handler "/hello-world" "http://localhost:9000/hello") ``` would return a ring handler that proxies all requests with URL prefix "/hello-world" to `http://localhost:9000/hello`. The following: ```clj (wrap-proxy handler #"^/hello-world" "http://localhost:9000/hello") ``` would return a ring handler that proxies all requests with a URL path matching the regex `#^/hello-world"` to `http://localhost:9000/hello/[url-path]`. #### Proxy Redirect Support By default, all proxy requests using `wrap-proxy` will follow any redirects, including on POST and PUT requests. To allow redirects but restrict their use on POST and PUT requests, set the `:force-redirects` option to `false` in the `http-opts` map. To disable redirect following on proxy requests, set the `:follow-redirects` option to `false` in the `http-opts` map. Please not that if proxy redirect following is disabled, you may have to disable it on the client making the proxy request as well if the location returned by the redirect is relative. #### SSL Support `wrap-proxy` supports SSL. To add SSL support, you can set SSL options in the `http-opts` map as you would in a request made with [clj-http-client](https://github.com/puppetlabs/clj-http-client). Simply set the `:ssl-cert`, `:ssl-key`, and `:ssl-ca-cert` options in the `http-opts` map to be paths to your .pem files. ### wrap-with-certificate-cn This middleware adds a `:ssl-client-cn` key to the request map if a `:ssl-client-cert` is present. If no client certificate is present, the key's value is set to nil. This makes for easier certificate whitelisting (using the cert whitelisting function from pl/kitchensink) ### wrap-add-cache-headers A utility middleware with the following signature: ```clj (wrap-add-cache-headers handler) ``` This middleware adds `Cache-Control: no-store` headers to `GET` and `PUT` requests if they are handled by the handler. ### wrap-add-x-frame-options-deny A utility middleware with the following signature: ```clj (wrap-add-x-frame-options-deny handler) ``` This middleware adds `X-Frame-Options: DENY` headers to requests if they are handled by the handler. ### wrap-add-x-content-nosniff A utility middleware with the following signature: ```clj (wrap-add-x-content-nosniff handler) ``` This middleware adds `X-Content-Type-Options: nosniff` headers to requests if they are handled by the handler. ### wrap-add-csp A utility middleware with the following signature: ```clj (wrap-add-csp handler csp-val) ``` This middleware adds `Content-Security-Policy` headers to requests if they are handled by the handler. The value of the header will be equivalent to the second argument passed, `csp-val`. ### wrap-params A utility middleware with the following signature: ```clj (wrap-params handler & [options]) ``` This middleware parses url-encoded parameters from the query string and form body and adds the following keys to the request map: * `:query-params` - a map of parameters from the query string * `:form-params` - a map of parameters from the body * `:params` - a map of all types of parameter Accepts the following options: * `:encoding` - encoding to use for url-decoding. If not specified, uses the request character encoding, or "UTF-8" if no request charater encoding is set. ### wrap-data-errors ```clj (wrap-data-errors handler) ``` Always returns a status code of 400 to the client and logs the error message at the "error" log level. Catches and processes any exceptions thrown via `slingshot/throw+` with a `:type` of one of: * `:request-data-invalid` * `:user-data-invalid` * `:data-invalid` * `:service-status-version-not-found` Returns a basic ring response map with the `:body` set to the JSON serialized representation of the exception thrown wrapped in a map and accessible by the "error" key. Example return body: ```json { "error": { "type": "user-data-invalid", "message": "Error Message From Thrower" } } ``` Returns valid [`ResponseType`](#schemas)s, eg: ```clj (wrap-data-errors handler :plain) ``` ### wrap-bad-request ```clj (wrap-bad-request handler) ``` Always returns a status code of 400 to the client and logs the error message at the "error" log level. Catches and processes any exceptions thrown via `slingshot/throw+` with a `:type` of one of: * `:bad-request` Returns a basic ring response map with the `:body` set to the JSON serialized representation of the exception thrown wrapped in a map and accessible by the "error" key. Example return body: ```json { "error": { "type": "bad-request", "message": "Error Message From Thrower" } } ``` Returns valid [`ResponseType`](#schemas)s, eg: ```clj (wrap-bad-request handler :plain) ``` ### wrap-schema-errors ```clj (wrap-schema-errors handler) ``` Always returns a status code of 500 to the client and logs an message containing the schema error, expected value, and exception type at the "error" log level. Returns a basic ring response map with the `:body` as the JSON serialized representation of helpful exception information wrapped in a map and accessible by the "error" key. Always returns an error type of "application-error". Example return body: ```json { "error": { "type": "application-error", "message": "Something unexpected happened: {:error ... :value ... :type :schema.core/error}" } } ``` Returns valid [`ResponseType`](#schemas)s, eg: ```clj (wrap-schema-errors handler :plain) ``` ### wrap-uncaught-errors ```clj (wrap-uncaught-errors handler) ``` Always returns a status code of 500 to the client and logs a message with the serialized Exception at the "error" log level. Returns a basic ring response map with the `:body` set as the JSON serialized representation of helpful exception information wrapped in a map and accessible by the "error" key. Always returns an error type of "application-error". Example return body: ```json { "error": { "type": "application-error", "message": "Internal Server Error: " } } ``` Returns valid [`ResponseType`](#schemas)s, eg: ```clj (wrap-uncaught-errors handler :plain) ``` ## Support Please log tickets and issues at our [Trapperkeeper Issue Tracker](https://tickets.puppetlabs.com/browse/TK). In addition there is a #trapperkeeper channel on Freenode. ring-middleware-1.3.0/dev-resources/000077500000000000000000000000001374162567600173745ustar00rootroot00000000000000ring-middleware-1.3.0/dev-resources/Makefile.i18n000066400000000000000000000141111374162567600216100ustar00rootroot00000000000000# -*- 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.ring_middleware # The name of the POT file into which the gettext code strings (msgid) will be placed POT_NAME=ring-middleware.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 ring-middleware-1.3.0/dev-resources/config/000077500000000000000000000000001374162567600206415ustar00rootroot00000000000000ring-middleware-1.3.0/dev-resources/config/jetty/000077500000000000000000000000001374162567600220005ustar00rootroot00000000000000ring-middleware-1.3.0/dev-resources/config/jetty/ssl/000077500000000000000000000000001374162567600226015ustar00rootroot00000000000000ring-middleware-1.3.0/dev-resources/config/jetty/ssl/certs/000077500000000000000000000000001374162567600237215ustar00rootroot00000000000000ring-middleware-1.3.0/dev-resources/config/jetty/ssl/certs/ca.pem000066400000000000000000000037001374162567600250070ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFizCCA3OgAwIBAgIJAOzZsJDE7wuoMA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV BAYTAlVTMQswCQYDVQQIDAJPUjERMA8GA1UEBwwIUG9ydGxhbmQxFDASBgNVBAoM C1B1cHBldCwgSW5jMQ8wDQYDVQQDDAZwdXBwZXQwHhcNMTkwNjEwMjA1NjE2WhcN MjQwNjA4MjA1NjE2WjBUMQswCQYDVQQGEwJVUzELMAkGA1UECAwCT1IxETAPBgNV BAcMCFBvcnRsYW5kMRQwEgYDVQQKDAtQdXBwZXQsIEluYzEPMA0GA1UEAwwGcHVw cGV0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoRjVNqC3Ol+2ZcoW Tjy+eqPcLMZKVVxI3AM9SNmbYiep/jxuDRzQ68Co2xMV94q1azqMubob/uJyyQCp nwh8WR1hSXrY93zB4l9SFRv0H/lxQYyjpSrnn9xPJKfwOuThxQGSwANmBIxnJ21r aKVYhT/bvP7BDWySMnybb+GIFv9NPxMiL3accB9zVwWzw2JtjiRxSTltxzTQWQqD cpg38bdHkEWbv86Ulew+haxZ8xRUoG37y5+zZnjHhAgG4sN6OdzPfZbYkr0I7HD/ GWb2DqgVzIsETd9IINjuEoSkYDEm5iXmCprVgrm6q+1DNgTXV5Zuy7hCDFLOluk0 dIKj6bcvaDP6OC1Mf1gEWBYHWN8JSmnBdm+9fRLF4lAWSw/kckQ1ERZFanu7OTfU 0W0V+f+yA+Q2SZXrQoo9RO1LzY4aa0HgcLiXHV5ruzNpBTKQuyTh2cMSm7KFleDK 0M5KC5m4v/Ee0oWq5ziLAFtDz8kMdWQBccTuFvyw10HsrSDO1meWChvaU9MfIFnC 8A70f1miEG7Gd5CcSV2fC3t+NoMZIe6OhGe/Jsb35nOocCdqPZXPwzVQ6mqy6iDo b+917lj64Lf1Vd82h1MSLdrhawQemETki6lQQMhTiGg6QSVWc4SGku1LBVYk3+a5 HwRUEJLZxbAtJegVHE67RcOQW30CAwEAAaNgMF4wHQYDVR0OBBYEFPar3/mW0UfM Yjs/qCXZoiDqmnhZMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgGGMB8GA1Ud IwQYMBaAFPar3/mW0UfMYjs/qCXZoiDqmnhZMA0GCSqGSIb3DQEBCwUAA4ICAQBY Z7jI6VqHM0VW65Qsv7NVZemhzl/XfRT+krxNHR43MBpVDHhJhr9Xk2zXk0AGiEum nUF/nRElrSURu9Frq0vM1J2xR8M0DXhG3ukv7sZHJfZ7NmlLk3Nx2/Z2R4ywpI4+ igJPKB22m8qxPVOXisI+HRfHus83YpxS9XeEgYtQe1qZIvirZ0sYiWmApR00R11a ybN1ls9lig7TcxBpL/Bt13T+XQzeewDjauUEv1jaUvq9KLadjBh5KbUFgK3Ns8FS a5EBGAfiRnuy1bMMUGtOew0dxJMZnYOYKx6EpMyVpJH82S3JN7bYuI86/7TtQLT/ X7hjf22Q+9Qr+lBytR/dwhQImqnjFKvvoV4mmSh914xxP89ylMzw6ZA7jDqKXFXu vLiXdPuWODT1slJfkl/vRMvadeCeDNFAz4nxP3IfgnzdyqCv8C90jTXqH0yxqGbP a6PXVNJyXSe7NdfBYch9MvOk1PveRXczvHp/6tb1vFAyOY7vl38Hv469TsuKPJjg WhTmXwaelWyTA/rxmrLkKk4awe6e3tfzQPBY/hj+dwWE1ryOQQ632hMTYhFCCgnA XxhZFwJhkWKVh3zGiRreB+vNPK91ZsC9z9cr/bqt3zOJnD3eOkdELi8oOkzcn+K7 /gFq3nI/TAOD3t23lc8DfB+XhfGETPVt+Wb/pVJtYg== -----END CERTIFICATE----- ring-middleware-1.3.0/dev-resources/config/jetty/ssl/certs/localhost.pem000066400000000000000000000027351374162567600264230ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEJzCCAg8CCQD1LykrEEjpTzANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJV UzELMAkGA1UECAwCT1IxETAPBgNVBAcMCFBvcnRsYW5kMRQwEgYDVQQKDAtQdXBw ZXQsIEluYzEPMA0GA1UEAwwGcHVwcGV0MB4XDTE5MDYxMDIwNTYxNloXDTI0MDYw ODIwNTYxNlowVzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk9SMREwDwYDVQQHDAhQ b3J0bGFuZDEUMBIGA1UECgwLUHVwcGV0LCBJbmMxEjAQBgNVBAMMCWxvY2FsaG9z dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK57VOCcL3c6uQk/mhnH b5OOMdGQaIF4nSiEKlMxIDeL7jKqPCx+QPXnvr/eCSXwW2oSIoXEGLJZHwi5/8pC H0xILMZas9XzgDBpvuMxbwHQtkaBg6ok7OU0h2KTY01V7kQeWNu4+4wut8XBbAzH 58P7X+pgrkkMXFXyW5bVVgBrXQVA+oC4990+LsrPGRi3dYO+wbU9QiIy0WnNqCc/ WnZ/c/pcCkCNlsZmlYspfnsKrRUW8+qzx0Ei1zyxRLdgg0RzLZe3uGh+MszkIEXY w2UG1RliZZdvsJ2osKeUDN6jUcYKhsxdOVWI7LMfz5T4eX8sjjGC4aF2JwXAXdfB hMkCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAWu7qoX1JbihvKMks7TjBaGnellZq 4fT1omO5+NZAfc2xW8Xez0lB0gzPPEEtCPuULivtwwmqQriTO7IJ8ZtpjHGwzrEF RWdrmeHQSF9PZNrZocV8WYWDX9liCHZrekDAWGBWtWSP3Dypmt1lILtJo4m5sJzT cxylSMD7+3FyHbBsvexSWoyI8kMckXIQ/SjImeJYmOHDiR7yUzpaHjvnmTuTx1sB NnyyG68gXj0S+SLdjY0A8LzhsvKTc850eUNYhcfEkIRQEVi6LU+uw8Hflk5O4Pf5 ClxmsHXqobKzAkKDu1jZuolFc21w85KEA2z3lojlmhxPBh7e1zWpz6lxDXSyMD4Y UgS44X+vaWbN6V/Er5LGeI9WkDPwNK1s5XpJe6qsWFMEC0xoQxDojT/JzHZmPFCs 1+j4AbPngu0GX8jab27dC/6ZuUmdO/HcIfnook8dxrZNaYnkyDzcGbm8Fhbn9QJo Y1wIOmwstrkdzMltkxjv+rw7HZiGxzopY+HQdjlGQGeLbSxgDwRi7hhbwUp7sGb3 jOjnRGC+IV4+dsJyJXIR+K6RdIfYdwzTZNUFBmrdaNdxDOHPmE8ot63Ty9ixqDSw CaY9ZpzBYouWXwpGstfpUJSO9rHqkToIpYuDMdh77EX66oPAsXOlNN70g/s12/X+ 5NRSoyCNqrVFFQk= -----END CERTIFICATE----- ring-middleware-1.3.0/dev-resources/config/jetty/ssl/private_keys/000077500000000000000000000000001374162567600253065ustar00rootroot00000000000000ring-middleware-1.3.0/dev-resources/config/jetty/ssl/private_keys/localhost.pem000066400000000000000000000032131374162567600300000ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEArntU4Jwvdzq5CT+aGcdvk44x0ZBogXidKIQqUzEgN4vuMqo8 LH5A9ee+v94JJfBbahIihcQYslkfCLn/ykIfTEgsxlqz1fOAMGm+4zFvAdC2RoGD qiTs5TSHYpNjTVXuRB5Y27j7jC63xcFsDMfnw/tf6mCuSQxcVfJbltVWAGtdBUD6 gLj33T4uys8ZGLd1g77BtT1CIjLRac2oJz9adn9z+lwKQI2WxmaViyl+ewqtFRbz 6rPHQSLXPLFEt2CDRHMtl7e4aH4yzOQgRdjDZQbVGWJll2+wnaiwp5QM3qNRxgqG zF05VYjssx/PlPh5fyyOMYLhoXYnBcBd18GEyQIDAQABAoIBAGmvKmoiMF33o7yG WcDjnCG0L1jQL3y8ofS4mSpqxyjeCRmHI4uPralFBbURuFjSjYxiD6cWcB8W/EU8 5EKOaoa58OT0a5lcNHkelBDpEGP5F279Y9LbKuJqPtbmj294bSm1d/0evr7aTVXr IpdbPnbm++HrTaXRZtxoQDHMZps5a85j4//2Fl1Hcc4MmScldzb3gmPpRdjSGUHU C5zCcIHJ4NfmXZvK2kVBsUbHbU31Q/+P+RYQ/V2Jl8gytXo7rAAHBfy/9fw9C4Ia 0U/fypbSRuPhmKNNhzcnoWNLdVmMXHDCy6LOGwJzy0htWcImQSK7fm/IorKuFTLw ufMRBEECgYEA5pAzU/FQylPNFj9xCSD2fXee29JtZNZ9KSpVZFU9BfIEyygHy2hx wEXnrEiXyC6Q6y66WYAbL8MrVFts4E8wMFE5SYDkRi2hzOVJ6dRRvowFykqgvmIH J+bo695imV0Lr+ypgJwS1KTbIns24b6MKFehPF7ba4AQMisOkpBdV1MCgYEAwbs4 dOdbptwbQKXK+ns4KEHkTOSyf0gMAk1qw6kOutlVL8Zqmda2eM9awSuAw3i5naar 5WRxdnuMeZTyRcfvsMWz0N9gsA/QRorMi2X2sy+y8lAyqYzeZ97/lanYc17pUMPR hKgGTkurU/5hbyFwHRd4HqyIl+bjRkKQEqhqu/MCgYBkmHHspBMt09ZdaJy28Oza 3UAwhOU54+OD/HZ5aj9PwsKYqQSNmSpYS6fDIYEn1Dlig2bG+f/inPLgwWsFi0dp LWcjTNtMS0iXD0tqfDqWOp2kH1928WpR3Iuvrb2zUEdzIHzsIiyNHsiFiODvKaYZ S/KLVo/BnYRzfxnClL5FBQKBgQCS6Qi9Dh0ggdVgxtkP7ZT+YklgTiVWBCqnLFIE IhV7eAfzfMGl+37QSl4GEplIoioP9S5xfCPWt4YlROAcLqwDH0lm4Z4OpU9Jz0Oh iib0Zv2KZ2qjzKFzn1WuBuiPRLAlgPlHYau2gAdiYIs+nNrKFNwQVsIWfw5mX1+l 5nVaJQKBgDMHcaAARv+R8PJI/bdj6+VUJXiUzE74cHp49jKGnfSBaL5+Kas3wJEF SD7GzNQlrFhU1E0zV428FNH9UiUsbcI5yQ834L3pxq9jVCqz6Tfgu747M87ZoBor TaeoVI49U0j421QC/BqFHcIBsRcneCLVjqw4M9LfshgkOpvFTYai -----END RSA PRIVATE KEY----- ring-middleware-1.3.0/dev-resources/exts.cnf000066400000000000000000000017031374162567600210500ustar00rootroot00000000000000[ 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 ring-middleware-1.3.0/dev-resources/gen-pki.sh000077500000000000000000000026201374162567600212650ustar00rootroot00000000000000#!/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} ring-middleware-1.3.0/dev-resources/logback-test.xml000066400000000000000000000004521374162567600224760ustar00rootroot00000000000000 %d %-5p [%c{2}] %m%n ring-middleware-1.3.0/dev-resources/ssl/000077500000000000000000000000001374162567600201755ustar00rootroot00000000000000ring-middleware-1.3.0/dev-resources/ssl/cert.pem000066400000000000000000000035471374162567600216460ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFSjCCAzKgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRQdXBw ZXQgQ0E6IGV4cGxvc2l2bzAeFw0xMzAxMDEyMTQ0MjhaFw0xODAxMDEyMTQ0Mjha MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC AgoCggIBANCEYBvEIdTXFOMwz18wahs6tg26C+LT2XOwQspb/Aj5WT2EBwnG2leD CzfOAKyhHDL6jRqrYU32jqoqBzmzkeVsHzqOMNFosyvcBLU8zyLZU+IP1rjJCyE8 xx9HsdhPKJj93f/gSNR5NQlRcZfqahhOwh/nYdY3pFiNgjUoRwhV2Q01n+ku8WJw kLVT1TREW9TiSWk7cHWF/ZltPOMMxvJ9q0kXh8sVYK4Gtt3pphTUW0qgXQ2NnNWT W+7vciRjnHxeoY3q6ZG7vZ8HewYKR4W8D6FA32xCsWELSsWlAABt1lBjKGas/fiY SeDqSfIxknFn/CIM9AIp2PLS3wh5e0o98qey9AN2WRyG7Qs0ijhwKx9bsMxbM0LR 5jXuXjBnjGQ69fwCjwlUsOSpNPWLibM+GmxvhghJgAlH5dD4+GqN77WLncQTWYXX GnOw5efVivS4bgU3t8l8mHLH6quLolR1KLfCv+HuqkvRposAqqLwKH+dhlbq1Y+i 4siPxfYV5NZ092Z9R0F4BPEmLhKngkK+/eQXxLY2zfaR6Ns83yRJfMXRyElECX/+ RBT1LyIRZg+MbsRg7DsKWI0plzxso/4CgSmYSfPku5nkekrMN34YhUtcxsdHSmY1 5/p2olvKpTJj3e5fa2KVswcv77FsC17gIfMXqvN3tITP+q1LLJHNAgMBAAGjgZsw gZgwNwYJYIZIAYb4QgENBCoWKFB1cHBldCBSdWJ5L09wZW5TU0wgSW50ZXJuYWwg Q2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQGCCsGAQUF BwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT3awXrPXSWNszZ tlLKoXQ8LRjJ3DANBgkqhkiG9w0BAQsFAAOCAgEAYTFx++uptZxgptFmkPfT1f2W 6djOOVULmlLGPC6Ovbe5v0ksA2hbLW3eSmfL28Ku0WC8gRl0/PhyiyW77M1jp9dV ztsFkXjMiIIcY0B7Hgqh1kpK1CFvSbsD3piXcDLlZ1CwSAXuohp+J2fUblHRfAUD Th9qrm3g4uNFp0wXxO1+GgXeDrGRqYosb0wAhB7/BhW2WbOFtVYdFoyyXJFJYx/3 Gj7ZTE3rvGGxOEEuww0pFmuGCflZYxEu15Rynej7soGaE80+wRk+gMS26WKwRQ/0 TGovOHSLo/fpOjjHIoqbQLH3S08jUfAYjjP7Rd01SiztUjZMILC2WCnpdYN+2O9O rGTj2Zl3oRtE67NwxgKlo2GIFghSF366XOF8O4z1e9id6u5XEdoz8uGFkHyMu79N cdYcUtmAqLvJ0Ubewg+TfNDcfk1akNtHtIJDNqFwHlZ9R1GIupHQs10R4YxJv3I2 LojJbtcgWcDg9StwCHRA0SuLrWnHPnm+glzXM5HTNJZ6vcrM0SrcnZ59p3o3ZULL JTJikA+pcs+WAWz1yTNg/ywxYPnFrs8A4MEC43XFe2dS96a7VcNqpMMya+DAWWCS +51lDlMLuz+q5WHDGh3ouTNYMdcSLo8bHzKInDYnK/DzUpdJ5OKYwqfxv5n0bJs6 fRA2buaQo0zJeid7G2Q= -----END CERTIFICATE----- ring-middleware-1.3.0/ext/000077500000000000000000000000001374162567600154065ustar00rootroot00000000000000ring-middleware-1.3.0/ext/travisci/000077500000000000000000000000001374162567600172325ustar00rootroot00000000000000ring-middleware-1.3.0/ext/travisci/test.sh000077500000000000000000000000271374162567600205470ustar00rootroot00000000000000#!/bin/bash lein test ring-middleware-1.3.0/jenkins/000077500000000000000000000000001374162567600162475ustar00rootroot00000000000000ring-middleware-1.3.0/jenkins/deploy.sh000077500000000000000000000003551374162567600201050ustar00rootroot00000000000000#!/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:$RING_MIDDLEWARE_BRANCH echo "git push successful." ring-middleware-1.3.0/locales/000077500000000000000000000000001374162567600162305ustar00rootroot00000000000000ring-middleware-1.3.0/locales/eo.po000066400000000000000000000026721374162567600172020ustar00rootroot00000000000000# Esperanto translations for puppetlabs.ring_middleware package. # Copyright (C) 2017 Puppet # This file is distributed under the same license as the puppetlabs.ring_middleware package. # Automatically generated, 2017. # msgid "" msgstr "" "Project-Id-Version: puppetlabs.ring_middleware \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/puppetlabs/ring_middleware/common.clj msgid "" "Proxying request to {0} to remote url {1}. Remote server responded with " "status {2}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Processing {0} {1}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Full request:" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Computed response: {0}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Submitted data is invalid: {0}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Service Unavailable:" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Bad Request:" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Something unexpected happened: {0}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Internal Server Error: {0}" msgstr "" ring-middleware-1.3.0/locales/ring-middleware.pot000066400000000000000000000026271374162567600220350ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Puppet # This file is distributed under the same license as the puppetlabs.ring_middleware package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: puppetlabs.ring_middleware \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/puppetlabs/ring_middleware/common.clj msgid "" "Proxying request to {0} to remote url {1}. Remote server responded with " "status {2}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Processing {0} {1}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Full request:" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Computed response: {0}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Submitted data is invalid: {0}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Service Unavailable:" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Bad Request:" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Something unexpected happened: {0}" msgstr "" #: src/puppetlabs/ring_middleware/core.clj msgid "Internal Server Error: {0}" msgstr "" ring-middleware-1.3.0/project.clj000066400000000000000000000027741374162567600167600ustar00rootroot00000000000000(defproject puppetlabs/ring-middleware "1.3.0" :dependencies [[org.clojure/clojure] [org.clojure/tools.logging] [cheshire] [prismatic/schema] [ring/ring-core] [slingshot] [puppetlabs/http-client] [puppetlabs/kitchensink] [puppetlabs/ssl-utils] [puppetlabs/i18n]] :min-lein-version "2.7.1" :parent-project {:coords [puppetlabs/clj-parent "0.4.3"] :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 :plugins [[lein-parent "0.3.1"] [puppetlabs/i18n "0.7.1"]] :deploy-repositories [["releases" {:url "https://clojars.org/repo" :username :env/clojars_jenkins_username :password :env/clojars_jenkins_password :sign-releases false}] ["snapshots" "http://nexus.delivery.puppetlabs.net/content/repositories/snapshots/"]] :profiles {:dev {:dependencies [[puppetlabs/trapperkeeper-webserver-jetty9] [puppetlabs/kitchensink nil :classifier "test" :scope "test"] [puppetlabs/trapperkeeper nil :classifier "test" :scope "test"] [compojure]]}}) ring-middleware-1.3.0/src/000077500000000000000000000000001374162567600153755ustar00rootroot00000000000000ring-middleware-1.3.0/src/puppetlabs/000077500000000000000000000000001374162567600175545ustar00rootroot00000000000000ring-middleware-1.3.0/src/puppetlabs/ring_middleware/000077500000000000000000000000001374162567600227105ustar00rootroot00000000000000ring-middleware-1.3.0/src/puppetlabs/ring_middleware/common.clj000066400000000000000000000046241374162567600247000ustar00rootroot00000000000000(ns puppetlabs.ring-middleware.common (:require [clojure.tools.logging :as log] [clojure.string :refer [join split replace-first]] [puppetlabs.http.client.sync :refer [request]] [puppetlabs.i18n.core :as i18n :refer [trs]]) (:import (java.net URI))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Private utility functions (defn prepare-cookies "Removes the :domain and :secure keys and converts the :expires key (a Date) to a string in the ring response map resp. Returns resp with cookies properly munged." [resp] (let [prepare #(-> (update-in % [1 :expires] str) (update-in [1] dissoc :domain :secure))] (assoc resp :cookies (into {} (map prepare (:cookies resp)))))) (defn strip-trailing-slash [url] (if (.endsWith url "/") (.substring url 0 (- (count url) 1)) url)) (defn proxy-request [req proxied-path remote-uri-base & [http-opts]] ; Remove :decompress-body from the options map, as if this is ; ever set to true, the response returned to the client making the ; proxy request will be truncated (let [http-opts (dissoc http-opts :decompress-body) uri (URI. (strip-trailing-slash remote-uri-base)) remote-uri (URI. (.getScheme uri) (.getAuthority uri) (str (.getPath uri) (if (instance? java.util.regex.Pattern proxied-path) (:uri req) (replace-first (:uri req) proxied-path ""))) nil nil) response (-> (merge {:method (:request-method req) :url (str remote-uri "?" (:query-string req)) :headers (dissoc (:headers req) "host" "content-length") :body (not-empty (slurp (:body req))) :as :stream :force-redirects false :follow-redirects false :decompress-body false} http-opts) request prepare-cookies)] (log/debug (trs "Proxying request to {0} to remote url {1}. Remote server responded with status {2}" (:uri req) (str remote-uri) (:status response))) response)) ring-middleware-1.3.0/src/puppetlabs/ring_middleware/core.clj000066400000000000000000000223631374162567600243400ustar00rootroot00000000000000(ns puppetlabs.ring-middleware.core (:require [cheshire.core :as json] [clojure.tools.logging :as log] [puppetlabs.kitchensink.core :as ks] [puppetlabs.ring-middleware.common :as common] [puppetlabs.ring-middleware.utils :as utils] [puppetlabs.ssl-utils.core :as ssl-utils] [ring.middleware.cookies :as cookies] [ring.util.response :as rr] [schema.core :as schema] [puppetlabs.i18n.core :as i18n :refer [trs]] [slingshot.slingshot :as sling]) (:import (clojure.lang IFn ExceptionInfo) (java.io ByteArrayOutputStream PrintStream) (java.lang Exception) (java.util.regex Pattern) (java.security.cert X509Certificate))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Private (defn sanitize-client-cert "Given a ring request, return a map which replaces the :ssl-client-cert with just the certificate's Common Name at :ssl-client-cert-cn. Also, remove the copy of the certificate put on the request by TK-auth." [req] (-> (if-let [client-cert (:ssl-client-cert req)] (-> req (dissoc :ssl-client-cert) (assoc :ssl-client-cert-cn (ssl-utils/get-cn-from-x509-certificate client-cert))) req) (ks/dissoc-in [:authorization :certificate]))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Middleware (schema/defn ^:always-validate wrap-request-logging :- IFn "A ring middleware that logs the request." [handler :- IFn] (fn [{:keys [request-method uri] :as req}] (log/debug (trs "Processing {0} {1}" request-method uri)) (log/trace (format "%s\n%s" (trs "Full request:") (ks/pprint-to-string (sanitize-client-cert req)))) (handler req))) (schema/defn ^:always-validate wrap-response-logging :- IFn "A ring middleware that logs the response." [handler :- IFn] (fn [req] (let [resp (handler req)] (log/trace (trs "Computed response: {0}" resp)) resp))) (schema/defn ^:always-validate wrap-proxy :- IFn "Proxies requests to proxied-path, a local URI, to the remote URI at remote-uri-base, also a string." [handler :- IFn proxied-path :- (schema/either Pattern schema/Str) remote-uri-base :- schema/Str & [http-opts]] (let [proxied-path (if (instance? Pattern proxied-path) (re-pattern (str "^" (.pattern proxied-path))) proxied-path)] (cookies/wrap-cookies (fn [req] (if (or (and (string? proxied-path) (.startsWith ^String (:uri req) (str proxied-path "/"))) (and (instance? Pattern proxied-path) (re-find proxied-path (:uri req)))) (common/proxy-request req proxied-path remote-uri-base http-opts) (handler req)))))) (schema/defn ^:always-validate wrap-add-cache-headers :- IFn "Adds cache control invalidation headers to GET and PUT requests if they are handled by the handler" [handler :- IFn] (fn [request] (let [request-method (:request-method request) response (handler request)] (when-not (nil? response) (if (or (= request-method :get) (= request-method :put)) (assoc-in response [:headers "cache-control"] "no-store") response))))) (schema/defn ^:always-validate wrap-add-x-frame-options-deny :- IFn "Adds 'X-Frame-Options: DENY' headers to requests if they are handled by the handler" [handler :- IFn] (fn [request] (let [response (handler request)] (when response (assoc-in response [:headers "X-Frame-Options"] "DENY"))))) (schema/defn ^:always-validate wrap-add-x-content-nosniff :- IFn "Adds 'X-Content-Type-Options: nosniff' headers to request." [handler :- IFn] (fn [request] (let [response (handler request)] (when response (assoc-in response [:headers "X-Content-Type-Options"] "nosniff"))))) (schema/defn ^:always-validate wrap-add-csp :- IFn "Adds 'Content-Security-Policy: default-src 'self'' headers to request." [handler :- IFn csp-val] (fn [request] (let [response (handler request)] (when response (assoc-in response [:headers "Content-Security-Policy"] csp-val))))) (schema/defn ^:always-validate wrap-with-certificate-cn :- IFn "Ring middleware that will annotate the request with an :ssl-client-cn key representing the CN contained in the client certificate of the request. If no client certificate is present, the key's value is set to nil." [handler :- IFn] (fn [{:keys [ssl-client-cert] :as req}] (let [cn (some-> ssl-client-cert ssl-utils/get-cn-from-x509-certificate) req (assoc req :ssl-client-cn cn)] (handler req)))) (schema/defn ^:always-validate wrap-data-errors :- IFn "A ring middleware that catches a slingshot error thrown by throw-data-invalid! or a :kind of slingshot error of one of: :request-data-invalid :user-data-invalid :data-invalid :service-status-version-not-found logs the error and returns a 400 ring response." ([handler :- IFn] (wrap-data-errors handler :json)) ([handler :- IFn type :- utils/ResponseType] (let [code 400 response (fn [e] (log/error e (trs "Submitted data is invalid: {0}" (:msg e))) (case type :json (utils/json-response code e) :plain (utils/plain-response code (:msg e))))] (fn [request] (sling/try+ (handler request) (catch #(contains? #{:request-data-invalid :user-data-invalid :data-invalid :service-status-version-not-found} (:kind %)) e (response e))))))) (schema/defn ^:always-validate wrap-service-unavailable :- IFn "A ring middleware that catches slingshot errors thrown by utils/throw-service-unavailabe!, logs the error and returns a 503 ring response." ([handler :- IFn] (wrap-service-unavailable handler :json)) ([handler :- IFn type :- utils/ResponseType] (let [code 503 response (fn [e] (log/error e (trs "Service Unavailable:" (:msg e))) (case type :json (utils/json-response code e) :plain (utils/plain-response code (:msg e))))] (fn [request] (sling/try+ (handler request) (catch utils/service-unavailable? e (response e))))))) (schema/defn ^:always-validate wrap-bad-request :- IFn "A ring middleware that catches slingshot errors thrown by utils/throw-bad-request!, logs the error and returns a 503 ring response." ([handler :- IFn] (wrap-bad-request handler :json)) ([handler :- IFn type :- utils/ResponseType] (let [code 400 response (fn [e] (log/error e (trs "Bad Request:" (:msg e))) (case type :json (utils/json-response code e) :plain (utils/plain-response code (:msg e))))] (fn [request] (sling/try+ (handler request) (catch utils/bad-request? e (response e))))))) (schema/defn ^:always-validate wrap-schema-errors :- IFn "A ring middleware that catches schema errors and returns a 500 response with the details" ([handler :- IFn] (wrap-schema-errors handler :json)) ([handler :- IFn type :- utils/ResponseType] (let [code 500 response (fn [e] (let [msg (trs "Something unexpected happened: {0}" (select-keys e [:error :value :type]))] (log/error e msg) (case type :json (utils/json-response code {:kind :application-error :msg msg}) :plain (utils/plain-response code msg))))] (fn [request] (sling/try+ (handler request) (catch utils/schema-error? e (response e))))))) (schema/defn ^:always-validate wrap-uncaught-errors :- IFn "A ring middleware that catches all otherwise uncaught errors and returns a 500 response with the error message" ([handler :- IFn] (wrap-uncaught-errors handler :json)) ([handler :- IFn type :- utils/ResponseType] (let [code 500 response (fn [e] (let [msg (trs "Internal Server Error: {0}" (.toString e)) baos (ByteArrayOutputStream.) _ (->> baos (PrintStream.) (.printStackTrace e)) log-msg (trs "Internal Server Error: {0}" (.toString baos))] (log/error log-msg) (case type :json (utils/json-response code {:kind :application-error :msg msg}) :plain (utils/plain-response code msg))))] (fn [request] (sling/try+ (handler request) (catch Exception e (response e))))))) ring-middleware-1.3.0/src/puppetlabs/ring_middleware/params.clj000066400000000000000000000115671374162567600246770ustar00rootroot00000000000000(ns puppetlabs.ring-middleware.params (:require [ring.util.codec :as codec] [ring.util.request :as req])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; COPY OF RELEVANT FUNCTIONS FROM UPSTREAM ring.middleware.params LIBRARY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This namespace is basically just here to provide an implementation of ;; the `params-request` middleware function that supports a String representation ;; of the body of a request. The upstream library requires the body to be of ;; a type that is compatible with Clojure's IOFactory, which forces us to read ;; the request body into memory twice for requests that we have to pass down ;; into the JRuby layer. (Technically, the Ring specification states that the ;; body must be an InputStream, so the maintainer of the upstream library was ;; reluctant to accept any sort of upstream PR to work around this issue.) ;; ;; All of this code is copied from the upstream library, and there is just ;; one very slight modification (see comment in `assoc-form-params` function) ;; that allows us to avoid reading the body into memory twice. ;; ;; In the future, if we can handle the query parameter parsing strictly on the ;; Clojure side and remove that code from the Ruby side, we should be able to ;; get rid of this. That will be much easier to consider doing once we're able ;; to get rid of the Rack/Webrick support. ;; ;; If that happens, we should delete this namespace :) ;; (defn parse-params [params encoding] (let [params (codec/form-decode params encoding)] (if (map? params) params {}))) (defn content-type "Return the content-type of the request, or nil if no content-type is set." [request] ;; NOTE: in the latest version of ring-core, they only look in ;; the headers map for the content type. They no longer fall ;; back to looking for it in the main request map. (if-let [type (or (get-in request [:headers "content-type"]) (get request :content-type))] (second (re-find #"^(.*?)(?:;|$)" type)))) (defn urlencoded-form? "True if a request contains a urlencoded form in the body." [request] (if-let [^String type (content-type request)] (.startsWith type "application/x-www-form-urlencoded"))) (defn assoc-query-params "Parse and assoc parameters from the query string with the request." [request encoding] (merge-with merge request (if-let [query-string (:query-string request)] (let [params (parse-params query-string encoding)] {:query-params params, :params params}) {:query-params {}, :params {}}))) (defn assoc-form-params "Parse and assoc parameters from the request body with the request." [request encoding] (merge-with merge request (if-let [body (and (urlencoded-form? request) (:body request))] (let [params (parse-params ;; NOTE: this is the main difference between our ;; copy of this code and the upstream version: ;; the upstream always does a slurp here, while ;; we only do a slurp if the body is not already ;; a string. (if (string? body) body (slurp body :encoding encoding)) encoding)] {:form-params params, :params params}) {:form-params {}, :params {}}))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Public (defn params-request "Adds parameters from the query string and the request body to the request map. See: wrap-params." {:arglists '([request] [request options])} [request & [opts]] (let [encoding (or (:encoding opts) (req/character-encoding request) "UTF-8") request (if (:form-params request) request (assoc-form-params request encoding))] (if (:query-params request) request (assoc-query-params request encoding)))) (defn wrap-params "Middleware to parse urlencoded parameters from the query string and form body (if the request is a url-encoded form). Adds the following keys to the request map: :query-params - a map of parameters from the query string :form-params - a map of parameters from the body :params - a merged map of all types of parameter Accepts the following options: :encoding - encoding to use for url-decoding. If not specified, uses the request character encoding, or \"UTF-8\" if no request character encoding is set." {:arglists '([handler] [handler options])} [handler & [options]] (fn [request] (handler (params-request request options)))) ring-middleware-1.3.0/src/puppetlabs/ring_middleware/utils.clj000066400000000000000000000045661374162567600245550ustar00rootroot00000000000000(ns puppetlabs.ring-middleware.utils (:require [schema.core :as schema] [ring.util.response :as rr] [slingshot.slingshot :as sling] [cheshire.core :as json]) (:import (java.security.cert X509Certificate))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Schemas (def ResponseType (schema/enum :json :plain)) (def RingRequest {:uri schema/Str (schema/optional-key :ssl-client-cert) (schema/maybe X509Certificate) schema/Keyword schema/Any}) (def RingResponse {:status schema/Int :headers {schema/Str schema/Any} :body schema/Any schema/Keyword schema/Any}) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Helpers (schema/defn ^:always-validate json-response :- RingResponse [status :- schema/Int body :- schema/Any] (-> body json/encode rr/response (rr/status status) (rr/content-type "application/json; charset=utf-8"))) (schema/defn ^:always-validate plain-response :- RingResponse [status :- schema/Int body :- schema/Str] (-> body rr/response (rr/status status) (rr/content-type "text/plain; charset=utf-8"))) (defn throw-bad-request! "Throw a :bad-request type slingshot error with the supplied message" [message] (sling/throw+ {:kind :bad-request :msg message})) (defn bad-request? [e] "Determine if the supplied slingshot error is for a bad request" (when (map? e) (= (:kind e) :bad-request))) (defn throw-service-unavailable! "Throw a :service-unavailable type slingshot error with the supplied message" [message] (sling/throw+ {:kind :service-unavailable :msg message})) (defn service-unavailable? [e] "Determine if the supplied slingshot error is for an unavailable service" (when (map? e) (= (:kind e) :service-unavailable))) (defn throw-data-invalid! "Throw a :data-invalid type slingshot error with the supplied message" [message] (sling/throw+ {:kind :data-invalid :msg message})) (defn data-invalid? [e] "Determine if the supplied slingshot error is for invalid data" (when (map? e) (= (:kind e) :data-invalid))) (defn schema-error? [e] "Determine if the supplied slingshot error is for a schema mismatch" (when (map? e) (= (:type e) :schema.core/error))) ring-middleware-1.3.0/test/000077500000000000000000000000001374162567600155655ustar00rootroot00000000000000ring-middleware-1.3.0/test/puppetlabs/000077500000000000000000000000001374162567600177445ustar00rootroot00000000000000ring-middleware-1.3.0/test/puppetlabs/ring_middleware/000077500000000000000000000000001374162567600231005ustar00rootroot00000000000000ring-middleware-1.3.0/test/puppetlabs/ring_middleware/core_test.clj000066400000000000000000001021161374162567600255620ustar00rootroot00000000000000(ns puppetlabs.ring-middleware.core-test (:require [cheshire.core :as json] [clojure.test :refer :all] [compojure.core :refer :all] [compojure.handler :as handler] [compojure.route :as route] [puppetlabs.ring-middleware.core :as core] [puppetlabs.ring-middleware.utils :as utils] [puppetlabs.ring-middleware.testutils.common :refer :all] [puppetlabs.ssl-utils.core :refer [pem->cert]] [puppetlabs.ssl-utils.simple :as ssl-simple] [puppetlabs.trapperkeeper.app :refer [get-service]] [puppetlabs.trapperkeeper.services.webserver.jetty9-service :refer :all] [puppetlabs.trapperkeeper.testutils.bootstrap :refer [with-app-with-config]] [puppetlabs.trapperkeeper.testutils.logging :as logutils] [ring.util.response :as rr] [schema.core :as schema] [slingshot.slingshot :as slingshot])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Testing Helpers (def WackSchema [schema/Str]) (schema/defn ^:always-validate cause-schema-error [request :- WackSchema] (throw (IllegalStateException. "The test should have never gotten here..."))) (defn throwing-handler [kind msg] (fn [_] (slingshot/throw+ {:kind kind :msg msg}))) (defn basic-request ([] (basic-request "foo-agent" :get "https://example.com")) ([subject method uri] {:request-method method :uri uri :ssl-client-cert (:cert (ssl-simple/gen-self-signed-cert subject 1 {:keylength 512})) :authorization {:certificate "foo"}})) (defn post-target-handler [req] (if (= (:request-method req) :post) {:status 200 :body (slurp (:body req))} {:status 404 :body "Z'oh"})) (defn proxy-target-handler [req] (condp = (:uri req) "/hello" {:status 302 :headers {"Location" "/hello/world"}} "/hello/" {:status 302 :headers {"Location" "/hello/world"}} "/hello/world" {:status 200 :body "Hello, World!"} "/hello/wrong-host" {:status 302 :headers {"Location" "http://localhost:4/fake"}} "/hello/fully-qualified" {:status 302 :headers {"Location" "http://localhost:9000/hello/world"}} "/hello/different-path" {:status 302 :headers {"Location" "http://localhost:9000/different/"}} {:status 404 :body "D'oh"})) (defn non-proxy-target [_] {:status 200 :body "Non-proxied path"}) (def gzip-body (apply str (repeat 1000 "f"))) (defn proxy-gzip-response [_] (-> gzip-body (rr/response) (rr/status 200) (rr/content-type "text/plain") (rr/charset "UTF-8"))) (defn proxy-error-handler [_] {:status 404 :body "N'oh"}) (defn proxy-regex-response [req] {:status 200 :body (str "Proxied to " (:uri req))}) (defroutes fallthrough-routes (GET "/hello/world" [] "Hello, World! (fallthrough)") (GET "/goodbye/world" [] "Goodbye, World! (fallthrough)") (route/not-found "Not Found (fallthrough)")) (def proxy-regex-fallthrough (handler/site fallthrough-routes)) (def proxy-wrapped-app (-> proxy-error-handler (core/wrap-proxy "/hello-proxy" "http://localhost:9000/hello"))) (def proxy-wrapped-app-ssl (-> proxy-error-handler (core/wrap-proxy "/hello-proxy" "https://localhost:9001/hello" {:ssl-cert "./dev-resources/config/jetty/ssl/certs/localhost.pem" :ssl-key "./dev-resources/config/jetty/ssl/private_keys/localhost.pem" :ssl-ca-cert "./dev-resources/config/jetty/ssl/certs/ca.pem"}))) (def proxy-wrapped-app-redirects (-> proxy-error-handler (core/wrap-proxy "/hello-proxy" "http://localhost:9000/hello" {:force-redirects true :follow-redirects true}))) (def proxy-wrapped-app-regex (-> proxy-regex-fallthrough (core/wrap-proxy #"^/([^/]+/certificate.*)$" "http://localhost:9000/hello"))) (def proxy-wrapped-app-regex-alt (-> proxy-regex-fallthrough (core/wrap-proxy #"/hello-proxy" "http://localhost:9000/hello"))) (def proxy-wrapped-app-regex-no-prepend (-> proxy-regex-fallthrough (core/wrap-proxy #"^/([^/]+/certificate.*)$" "http://localhost:9000"))) (def proxy-wrapped-app-regex-trailing-slash (-> proxy-regex-fallthrough (core/wrap-proxy #"^/([^/]+/certificate.*)$" "http://localhost:9000/"))) (defmacro with-target-and-proxy-servers [{:keys [target proxy proxy-handler ring-handler endpoint target-endpoint]} & body] `(with-app-with-config proxy-target-app# [jetty9-service] {:webserver ~target} (let [target-webserver# (get-service proxy-target-app# :WebserverService)] (add-ring-handler target-webserver# ~ring-handler ~target-endpoint) (add-ring-handler target-webserver# non-proxy-target "/different") (add-ring-handler target-webserver# post-target-handler "/hello/post")) (with-app-with-config proxy-app# [jetty9-service] {:webserver ~proxy} (let [proxy-webserver# (get-service proxy-app# :WebserverService)] (add-ring-handler proxy-webserver# ~proxy-handler ~endpoint)) ~@body))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Core Helpers (deftest sanitize-client-cert-test (testing "sanitize-client-cert" (let [subject "foo-client" cert (:cert (ssl-simple/gen-self-signed-cert subject 1)) request {:ssl-client-cert cert :authorization {:certificate "stuff"}} response (core/sanitize-client-cert request)] (testing "adds the CN at :ssl-client-cert-cn" (is (= subject (response :ssl-client-cert-cn)))) (testing "removes :ssl-client-cert key from response" (is (nil? (response :ssl-client-cert)))) (testing "remove tk-auth cert info" (is (nil? (get-in response [:authorization :certificate]))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; Core Middleware (deftest test-proxy (let [common-ssl-config {:ssl-cert "./dev-resources/config/jetty/ssl/certs/localhost.pem" :ssl-key "./dev-resources/config/jetty/ssl/private_keys/localhost.pem" :ssl-ca-cert "./dev-resources/config/jetty/ssl/certs/ca.pem"}] (testing "basic proxy support" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "http://localhost:9000/hello/world")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-get "http://localhost:10000/hello-proxy/world")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-get "http://localhost:10000/hello-proxy/world" {:as :stream})] (is (= (slurp (:body response)) "Hello, World!"))) (let [response (http-post "http://localhost:10000/hello-proxy/post/" {:as :stream :body "I'm posted!"})] (is (= (:status response) 200)) (is (= (slurp (:body response)) "I'm posted!"))))) (testing "basic https proxy support" (with-target-and-proxy-servers {:target (merge common-ssl-config {:ssl-host "0.0.0.0" :ssl-port 9001}) :proxy (merge common-ssl-config {:ssl-host "0.0.0.0" :ssl-port 10001}) :proxy-handler proxy-wrapped-app-ssl :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "https://localhost:9001/hello/world" default-options-for-https-client)] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-get "https://localhost:10001/hello-proxy/world" default-options-for-https-client)] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))))) (testing "basic http->https proxy support" (with-target-and-proxy-servers {:target (merge common-ssl-config {:ssl-host "0.0.0.0" :ssl-port 9001}) :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-ssl :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "https://localhost:9001/hello/world" default-options-for-https-client)] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-get "http://localhost:10000/hello-proxy/world")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))))) (testing "basic https->http proxy support" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy (merge common-ssl-config {:ssl-host "0.0.0.0" :ssl-port 10001}) :proxy-handler proxy-wrapped-app :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "http://localhost:9000/hello/world")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-get "https://localhost:10001/hello-proxy/world" default-options-for-https-client)] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))))) (testing "redirect test with proxy" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "http://localhost:9000/hello")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-get "http://localhost:9000/hello/world")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-get "http://localhost:10000/hello-proxy/" {:follow-redirects false :as :text})] (is (= (:status response) 302)) (is (= "/hello/world" (get-in response [:headers "location"])))) (let [response (http-post "http://localhost:10000/hello-proxy/" {:follow-redirects false :as :text})] (is (= (:status response) 302)) (is (= "/hello/world" (get-in response [:headers "location"])))) (let [response (http-get "http://localhost:10000/hello-proxy/world")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))))) (testing "proxy redirect succeeds on POST if :force-redirects set true" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-redirects :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "http://localhost:10000/hello-proxy/" {:follow-redirects false :as :text})] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))) (let [response (http-post "http://localhost:10000/hello-proxy/" {:follow-redirects false})] (is (= (:status response) 200))))) (testing "redirect test with fully qualified url, correct host, and proxied path" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-redirects :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "http://localhost:10000/hello-proxy/fully-qualified" {:follow-redirects false :as :text})] (is (= (:status response) 200)) (is (= (:body response) "Hello, World!"))))) (testing "redirect test with correct host on non-proxied path" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-redirects :ring-handler proxy-target-handler :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "http://localhost:9000/different")] (is (= (:status response) 200)) (is (= (:body response) "Non-proxied path"))) (let [response (http-get "http://localhost:10000/different")] (is (= (:status response) 404))) (let [response (http-get "http://localhost:10000/hello-proxy/different-path" {:follow-redirects false :as :text})] (is (= (:status response) 200)) (is (= (:body response) "Non-proxied path"))))) (testing "gzipped responses not truncated" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app :ring-handler proxy-gzip-response :endpoint "/hello-proxy" :target-endpoint "/hello"} (let [response (http-get "http://localhost:9000/hello")] (is (= gzip-body (:body response))) (is (= "gzip" (:orig-content-encoding response)))) (let [response (http-get "http://localhost:10000/hello-proxy/")] (is (= gzip-body (:body response))) (is (= "gzip" (:orig-content-encoding response)))))) (testing "proxy works with regex" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-regex :ring-handler proxy-regex-response :endpoint "/" :target-endpoint "/hello"} (let [response (http-get "http://localhost:10000/production/certificate/foo")] (is (= (:status response) 200)) (is (= (:body response) "Proxied to /hello/production/certificate/foo"))) (let [response (http-get "http://localhost:10000/hello/world")] (is (= (:status response) 200)) (is (= (:body response) "Hello, World! (fallthrough)"))) (let [response (http-get "http://localhost:10000/goodbye/world")] (is (= (:status response) 200)) (is (= (:body response) "Goodbye, World! (fallthrough)"))) (let [response (http-get "http://localhost:10000/production/cert/foo")] (is (= (:status response) 404)) (is (= (:body response) "Not Found (fallthrough)"))))) (testing "proxy regex matches beginning of string" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-regex-alt :ring-handler proxy-regex-response :endpoint "/" :target-endpoint "/hello"} (let [response (http-get "http://localhost:10000/hello-proxy")] (is (= (:status response) 200)) (is (= (:body response) "Proxied to /hello/hello-proxy"))) (let [response (http-get "http://localhost:10000/production/hello-proxy")] (is (= (:status response) 404)) (is (= (:body response) "Not Found (fallthrough)"))))) (testing "proxy regex does not need to match entire request uri" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-regex-alt :ring-handler proxy-regex-response :endpoint "/" :target-endpoint "/hello"} (let [response (http-get "http://localhost:10000/hello-proxy/world")] (is (= (:status response) 200)) (is (= (:body response) "Proxied to /hello/hello-proxy/world"))))) (testing "proxy works with regex and no prepended path" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-regex-no-prepend :ring-handler proxy-regex-response :endpoint "/" :target-endpoint "/"} (let [response (http-get "http://localhost:10000/production/certificate/foo")] (is (= (:status response) 200)) (is (= (:body response) "Proxied to /production/certificate/foo"))))) (testing "no repeat slashes exist in rewritten uri" (with-target-and-proxy-servers {:target {:host "0.0.0.0" :port 9000} :proxy {:host "0.0.0.0" :port 10000} :proxy-handler proxy-wrapped-app-regex-trailing-slash :ring-handler proxy-regex-response :endpoint "/" :target-endpoint "/"} (let [response (http-get "http://localhost:10000/production/certificate/foo")] (is (= (:status response) 200)) (is (= (:body response) "Proxied to /production/certificate/foo"))))))) (deftest test-wrap-add-cache-headers (let [put-request {:request-method :put} get-request {:request-method :get} post-request {:request-method :post} delete-request {:request-method :delete} no-cache-header "no-store"] (testing "wrap-add-cache-headers ignores nil response" (let [handler (constantly nil) wrapped-handler (core/wrap-add-cache-headers handler)] (is (nil? (wrapped-handler put-request))) (is (nil? (wrapped-handler get-request))) (is (nil? (wrapped-handler post-request))) (is (nil? (wrapped-handler delete-request))))) (testing "wrap-add-cache-headers observes handled response" (let [handler (constantly {}) wrapped-handler (core/wrap-add-cache-headers handler) handled-response {:headers {"cache-control" no-cache-header}} not-handled-response {}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= not-handled-response (wrapped-handler post-request))) (is (= not-handled-response (wrapped-handler delete-request))))) (testing "wrap-add-cache-headers doesn't stomp on existing headers" (let [fake-response {:headers {:something "Hi mom"}} handler (constantly fake-response) wrapped-handler (core/wrap-add-cache-headers handler) handled-response {:headers {:something "Hi mom" "cache-control" no-cache-header}} not-handled-response fake-response] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= not-handled-response (wrapped-handler post-request))) (is (= not-handled-response (wrapped-handler delete-request))))))) (deftest test-wrap-add-x-content-nosniff (let [put-request {:request-method :put} get-request {:request-method :get} post-request {:request-method :post} delete-request {:request-method :delete}] (testing "wrap-add-x-content-nosniff ignores nil response" (let [handler (constantly nil) wrapped-handler (core/wrap-add-x-content-nosniff handler)] (is (nil? (wrapped-handler put-request))) (is (nil? (wrapped-handler get-request))) (is (nil? (wrapped-handler post-request))) (is (nil? (wrapped-handler delete-request))))) (testing "wrap-add-x-content-nosniff observes handled response" (let [handler (constantly {}) wrapped-handler (core/wrap-add-x-content-nosniff handler) handled-response {:headers {"X-Content-Type-Options" "nosniff"}}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= handled-response (wrapped-handler post-request))) (is (= handled-response (wrapped-handler delete-request))))) (testing "wrap-add-x-content-nosniff doesn't stomp on existing headers" (let [fake-response {:headers {:something "Hi mom"}} handler (constantly fake-response) wrapped-handler (core/wrap-add-x-content-nosniff handler) handled-response {:headers {:something "Hi mom" "X-Content-Type-Options" "nosniff"}}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= handled-response (wrapped-handler post-request))) (is (= handled-response (wrapped-handler delete-request))))))) (deftest test-wrap-add-csp (let [put-request {:request-method :put} get-request {:request-method :get} post-request {:request-method :post} delete-request {:request-method :delete}] (testing "wrap-add-csp ignores nil response" (let [handler (constantly nil) wrapped-handler (core/wrap-add-csp handler "foo")] (is (nil? (wrapped-handler put-request))) (is (nil? (wrapped-handler get-request))) (is (nil? (wrapped-handler post-request))) (is (nil? (wrapped-handler delete-request))))) (testing "wrap-add-csp observes handled response" (let [handler (constantly {}) wrapped-handler (core/wrap-add-csp handler "foo") handled-response {:headers {"Content-Security-Policy" "foo"}}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= handled-response (wrapped-handler post-request))) (is (= handled-response (wrapped-handler delete-request))))) (testing "wrap-add-csp doesn't stomp on existing headers" (let [fake-response {:headers {:something "Hi mom"}} handler (constantly fake-response) wrapped-handler (core/wrap-add-csp handler "foo") handled-response {:headers {:something "Hi mom" "Content-Security-Policy" "foo"}}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= handled-response (wrapped-handler post-request))) (is (= handled-response (wrapped-handler delete-request))))) (testing "wrap-add-csp takes arg for header value" (let [handler (constantly {}) wrapped-handler (core/wrap-add-csp handler "bar") handled-response {:headers {"Content-Security-Policy" "bar"}}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= handled-response (wrapped-handler post-request))) (is (= handled-response (wrapped-handler delete-request))))))) (deftest test-wrap-with-cn (testing "When extracting a CN from a cert" (testing "and there is no cert" (let [mw-fn (core/wrap-with-certificate-cn identity) post-req (mw-fn {})] (testing "ssl-client-cn is set to nil" (is (= post-req {:ssl-client-cn nil}))))) (testing "and there is a cert" (let [mw-fn (core/wrap-with-certificate-cn identity) post-req (mw-fn {:ssl-client-cert (pem->cert "dev-resources/ssl/cert.pem")})] (testing "ssl-client-cn is set properly" (is (= (:ssl-client-cn post-req) "localhost"))))))) (deftest test-wrap-add-x-frame-options-deny (let [get-request {:request-method :get} put-request {:request-method :put} post-request {:request-method :post} delete-request {:request-method :delete} x-frame-header "DENY"] (testing "wrap-add-x-frame-options-deny ignores nil response" (let [handler (constantly nil) wrapped-handler (core/wrap-add-x-frame-options-deny handler)] (is (nil? (wrapped-handler get-request))) (is (nil? (wrapped-handler put-request))) (is (nil? (wrapped-handler post-request))) (is (nil? (wrapped-handler delete-request))))) (testing "wrap-add-x-frame-options-deny observes handled response" (let [handler (constantly {}) wrapped-handler (core/wrap-add-x-frame-options-deny handler) handled-response {:headers {"X-Frame-Options" x-frame-header}} not-handled-response {}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= handled-response (wrapped-handler post-request))) (is (= handled-response (wrapped-handler delete-request))))) (testing "wrap-add-x-frame-options-deny doesn't stomp on existing headers" (let [fake-response {:headers {:something "Hi mom"}} handler (constantly fake-response) wrapped-handler (core/wrap-add-x-frame-options-deny handler) handled-response {:headers {:something "Hi mom" "X-Frame-Options" x-frame-header}}] (is (= handled-response (wrapped-handler get-request))) (is (= handled-response (wrapped-handler put-request))) (is (= handled-response (wrapped-handler post-request))) (is (= handled-response (wrapped-handler delete-request))))))) (deftest wrap-response-logging-test (testing "wrap-response-logging" (logutils/with-test-logging (let [stack (core/wrap-response-logging identity) response (stack (basic-request))] (is (logged? #"Computed response.*" :trace)))))) (deftest wrap-request-logging-test (testing "wrap-request-logging" (logutils/with-test-logging (let [subject "foo-agent" method :get uri "https://example.com" stack (core/wrap-request-logging identity) request (basic-request subject method uri) response (stack request)] (is (logged? (format "Processing %s %s" method uri) :debug)) (is (logged? #"Full request" :trace)))))) (deftest wrap-data-errors-test (testing "wrap-data-errors" (testing "default behavior" (logutils/with-test-logging (let [stack (core/wrap-data-errors (throwing-handler :user-data-invalid "Error Message")) response (stack (basic-request)) json-body (json/parse-string (response :body))] (is (= 400 (response :status))) (is (= "Error Message" (get json-body "msg"))) (is (logged? #"Error Message" :error)) (is (logged? #"Submitted data is invalid" :error))))) (doseq [error [:request-data-invalid :user-data-invalid :service-status-version-not-found]] (testing (str "handles errors of " error) (logutils/with-test-logging (let [stack (core/wrap-data-errors (throwing-handler error "Error Message")) response (stack (basic-request)) json-body (json/parse-string (response :body))] (is (= 400 (response :status))) (is (= (name error) (get json-body "kind"))))))) (testing "handles errors thrown by `throw-data-invalid!`" (logutils/with-test-logging (let [stack (core/wrap-data-errors (fn [_] (utils/throw-data-invalid! "Error Message"))) response (stack (basic-request)) json-body (json/parse-string (response :body))] (is (= 400 (response :status))) (is (= (name "data-invalid") (get json-body "kind")))))) (testing "can be plain text" (logutils/with-test-logging (let [stack (core/wrap-data-errors (throwing-handler :user-data-invalid "Error Message") :plain) response (stack (basic-request))] (is (re-matches #"text/plain.*" (get-in response [:headers "Content-Type"])))))))) (deftest wrap-bad-request-test (testing "wrap-bad-request" (testing "default behavior" (logutils/with-test-logging (let [stack (core/wrap-bad-request (fn [_] (utils/throw-bad-request! "Error Message"))) response (stack (basic-request)) json-body (json/parse-string (response :body))] (is (= 400 (response :status))) (is (logged? #".*Bad Request.*" :error)) (is (re-matches #"Error Message.*" (get json-body "msg" ""))) (is (= "bad-request" (get json-body "kind")))))) (testing "can be plain text" (logutils/with-test-logging (let [stack (core/wrap-bad-request (fn [_] (utils/throw-bad-request! "Error Message")) :plain) response (stack (basic-request))] (is (re-matches #"text/plain.*" (get-in response [:headers "Content-Type"])))))))) (deftest wrap-schema-errors-test (testing "wrap-schema-errors" (testing "default behavior" (logutils/with-test-logging (let [stack (core/wrap-schema-errors cause-schema-error) response (stack (basic-request)) json-body (json/parse-string (response :body))] (is (= 500 (response :status))) (is (logged? #".*Something unexpected.*" :error)) (is (re-matches #"Something unexpected.*" (get json-body "msg" ""))) (is (= "application-error" (get json-body "kind")))))) (testing "can be plain text" (logutils/with-test-logging (let [stack (core/wrap-schema-errors cause-schema-error :plain) response (stack (basic-request))] (is (re-matches #"text/plain.*" (get-in response [:headers "Content-Type"])))))))) (deftest wrap-service-unavailable-test (testing "wrap-service-unavailable" (testing "default behavior" (logutils/with-test-logging (let [stack (core/wrap-service-unavailable (fn [_] (utils/throw-service-unavailable! "Test Service is DOWN!"))) response (stack (basic-request)) json-body (json/parse-string (response :body))] (is (= 503 (response :status))) (is (logged? #".*Service Unavailable.*" :error)) (is (= "Test Service is DOWN!" (get json-body "msg"))) (is (= "service-unavailable" (get json-body "kind")))))) (testing "can be plain text" (logutils/with-test-logging (let [stack (core/wrap-service-unavailable (fn [_] (utils/throw-service-unavailable! "Test Service is DOWN!")) :plain) response (stack (basic-request))] (is (re-matches #"text/plain.*" (get-in response [:headers "Content-Type"])))))))) (deftest wrap-uncaught-errors-test (testing "wrap-uncaught-errors" (testing "default behavior" (logutils/with-test-logging (let [stack (core/wrap-uncaught-errors (fn [_] (throw (IllegalStateException. "Woah...")))) response (stack (basic-request)) json-body (json/parse-string (response :body))] (is (= 500 (response :status))) ;; Note the "(?s)" uses Java's DOTALL matching mode, so that a "." will match newlines ;; This with the "at puppetlabs" that should be in a stacktrace ensures we're logging ;; the stacktrace... (is (logged? #"(?s).*Internal Server Error.*at puppetlabs.*" :error)) ;; ...while this ending anchor and lack of DOTALL ensures that we are not sending a ;; stacktrace (technically nothing with new lines). (is (re-matches #"Internal Server Error.*$" (get json-body "msg" "")))))) (testing "can be plain text" (logutils/with-test-logging (let [stack (core/wrap-uncaught-errors (fn [_] (throw (IllegalStateException. "Woah..."))) :plain) response (stack (basic-request))] (is (re-matches #"text/plain.*" (get-in response [:headers "Content-Type"])))))))) ring-middleware-1.3.0/test/puppetlabs/ring_middleware/testutils/000077500000000000000000000000001374162567600251405ustar00rootroot00000000000000ring-middleware-1.3.0/test/puppetlabs/ring_middleware/testutils/common.clj000066400000000000000000000012621374162567600271230ustar00rootroot00000000000000(ns puppetlabs.ring-middleware.testutils.common (:require [puppetlabs.http.client.sync :as http-client])) (def default-options-for-https-client {:ssl-cert "./dev-resources/config/jetty/ssl/certs/localhost.pem" :ssl-key "./dev-resources/config/jetty/ssl/private_keys/localhost.pem" :ssl-ca-cert "./dev-resources/config/jetty/ssl/certs/ca.pem" :as :text}) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Testing utility functions (defn http-get ([url] (http-get url {:as :text})) ([url options] (http-client/get url options))) (defn http-post ([url] (http-post url {:as :text})) ([url options] (http-client/post url options)))ring-middleware-1.3.0/test/puppetlabs/ring_middleware/utils_test.clj000066400000000000000000000017541374162567600260000ustar00rootroot00000000000000(ns puppetlabs.ring-middleware.utils-test (:require [cheshire.core :as json] [clojure.test :refer :all] [puppetlabs.ring-middleware.utils :as utils])) (deftest json-response-test (testing "json response" (let [source {:key 1} response (utils/json-response 200 source)] (testing "has 200 status code" (is (= 200 (:status response)))) (testing "has json content-type" (is (re-matches #"application/json.*" (get-in response [:headers "Content-Type"])))) (testing "is properly converted to a json string" (is (= 1 ((json/parse-string (:body response)) "key"))))))) (deftest plain-response-test (testing "json response" (let [message "Response message" response (utils/plain-response 200 message)] (testing "has 200 status code" (is (= 200 (:status response)))) (testing "has plain content-type" (is (re-matches #"text/plain.*" (get-in response [:headers "Content-Type"])))))))